mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-23 08:31:26 +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) {
|
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);
|
EntityItemFrame itemFrame = new EntityItemFrame(new Position(x, y, z), EntityItemFrame.ItemFrameOrientation.NORTH);
|
||||||
itemFrame.getPosition().setYaw(180f);
|
itemFrame.getPosition().setYaw(180f);
|
||||||
ItemStack map = new ItemStack(Material.FILLED_MAP, (byte) 1);
|
ItemStack map = ItemStack.builder(Material.FILLED_MAP)
|
||||||
map.setItemMeta(new MapMeta(id));
|
.meta(new MapMeta.Builder().mapId(id).build())
|
||||||
|
.build();
|
||||||
itemFrame.setItemStack(map);
|
itemFrame.setItemStack(map);
|
||||||
itemFrame.setInstance(instance);
|
itemFrame.setInstance(instance);
|
||||||
itemFrame.setCustomNameVisible(true);
|
itemFrame.setCustomNameVisible(true);
|
||||||
|
@ -60,7 +60,7 @@ public class Advancement {
|
|||||||
public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description,
|
public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description,
|
||||||
@NotNull Material icon, @NotNull FrameType frameType,
|
@NotNull Material icon, @NotNull FrameType frameType,
|
||||||
float x, float y) {
|
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,
|
public Advancement(@NotNull Component title, Component description,
|
||||||
@ -77,7 +77,7 @@ public class Advancement {
|
|||||||
public Advancement(@NotNull Component title, @NotNull Component description,
|
public Advancement(@NotNull Component title, @NotNull Component description,
|
||||||
@NotNull Material icon, @NotNull FrameType frameType,
|
@NotNull Material icon, @NotNull FrameType frameType,
|
||||||
float x, float y) {
|
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) {
|
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) {
|
public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull ItemStack icon) {
|
||||||
@ -46,7 +46,6 @@ public class Notification {
|
|||||||
* Gets the title of the notification.
|
* Gets the title of the notification.
|
||||||
*
|
*
|
||||||
* @return the notification title
|
* @return the notification title
|
||||||
*
|
|
||||||
* @deprecated Use {@link #getTitle()}
|
* @deprecated Use {@link #getTitle()}
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -10,12 +10,12 @@ import net.minestom.server.entity.EntityType;
|
|||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a hover event for a specific portion of the message.
|
* Represents a hover event for a specific portion of the message.
|
||||||
|
*
|
||||||
* @deprecated Use {@link HoverEvent}
|
* @deprecated Use {@link HoverEvent}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -93,11 +93,8 @@ public class ChatHoverEvent {
|
|||||||
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
|
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
|
||||||
obj = obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject();
|
obj = obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject();
|
||||||
|
|
||||||
if (itemStack.getItemMeta() != null) {
|
final String snbt = itemStack.getMeta().toSNBT();
|
||||||
NBTCompound compound = new NBTCompound();
|
obj.add("tag", new JsonPrimitive(snbt));
|
||||||
itemStack.getItemMeta().write(compound);
|
|
||||||
obj.add("tag", new JsonPrimitive(compound.toSNBT()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ChatHoverEvent("show_item", obj);
|
return new ChatHoverEvent("show_item", obj);
|
||||||
}
|
}
|
||||||
|
@ -42,13 +42,11 @@ public class ArgumentItemStack extends Argument<ItemStack> {
|
|||||||
if (nbtIndex == -1) {
|
if (nbtIndex == -1) {
|
||||||
// Only item name
|
// Only item name
|
||||||
final Material material = Registries.getMaterial(input);
|
final Material material = Registries.getMaterial(input);
|
||||||
return new ItemStack(material, (byte) 1);
|
return ItemStack.of(material);
|
||||||
} else {
|
} else {
|
||||||
final String materialName = input.substring(0, nbtIndex);
|
final String materialName = input.substring(0, nbtIndex);
|
||||||
final Material material = Registries.getMaterial(materialName);
|
final Material material = Registries.getMaterial(materialName);
|
||||||
|
|
||||||
ItemStack itemStack = new ItemStack(material, (byte) 1);
|
|
||||||
|
|
||||||
final String sNBT = input.substring(nbtIndex).replace("\\\"", "\"");
|
final String sNBT = input.substring(nbtIndex).replace("\\\"", "\"");
|
||||||
|
|
||||||
NBTCompound compound;
|
NBTCompound compound;
|
||||||
@ -58,9 +56,7 @@ public class ArgumentItemStack extends Argument<ItemStack> {
|
|||||||
throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT);
|
throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT);
|
||||||
}
|
}
|
||||||
|
|
||||||
NBTUtils.loadDataIntoItem(itemStack, compound);
|
return NBTUtils.loadItem(material, 1, compound);
|
||||||
|
|
||||||
return itemStack;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.minestom.server.data.type;
|
package net.minestom.server.data.type;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.data.DataType;
|
import net.minestom.server.data.DataType;
|
||||||
import net.minestom.server.inventory.Inventory;
|
import net.minestom.server.inventory.Inventory;
|
||||||
import net.minestom.server.inventory.InventoryType;
|
import net.minestom.server.inventory.InventoryType;
|
||||||
@ -15,7 +16,7 @@ public class InventoryData extends DataType<Inventory> {
|
|||||||
final int size = inventoryType.getSize();
|
final int size = inventoryType.getSize();
|
||||||
|
|
||||||
// Inventory title & type
|
// Inventory title & type
|
||||||
writer.writeSizedString(value.getTitle());
|
writer.writeComponent(value.getTitle());
|
||||||
writer.writeSizedString(inventoryType.name());
|
writer.writeSizedString(inventoryType.name());
|
||||||
|
|
||||||
// Write all item stacks
|
// Write all item stacks
|
||||||
@ -27,7 +28,7 @@ public class InventoryData extends DataType<Inventory> {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Inventory decode(@NotNull BinaryReader reader) {
|
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 InventoryType inventoryType = InventoryType.valueOf(reader.readSizedString(Integer.MAX_VALUE));
|
||||||
final int size = inventoryType.getSize();
|
final int size = inventoryType.getSize();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ public class ItemEntity extends ObjectEntity {
|
|||||||
if (!canApply)
|
if (!canApply)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
final ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount);
|
final ItemStack result = stackingRule.apply(itemStack, totalAmount);
|
||||||
|
|
||||||
EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result);
|
EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result);
|
||||||
callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> {
|
callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> {
|
||||||
|
@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
//TODO: Default attributes registration (and limitation ?)
|
//TODO: Default attributes registration (and limitation ?)
|
||||||
public class LivingEntity extends Entity implements EquipmentHandler {
|
public class LivingEntity extends Entity implements EquipmentHandler {
|
||||||
|
|
||||||
// Item pickup
|
// ItemStack pickup
|
||||||
protected boolean canPickupItem;
|
protected boolean canPickupItem;
|
||||||
protected Cooldown itemPickupCooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK));
|
protected Cooldown itemPickupCooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK));
|
||||||
|
|
||||||
@ -111,13 +111,13 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initEquipments() {
|
private void initEquipments() {
|
||||||
this.mainHandItem = ItemStack.getAirItem();
|
this.mainHandItem = ItemStack.AIR;
|
||||||
this.offHandItem = ItemStack.getAirItem();
|
this.offHandItem = ItemStack.AIR;
|
||||||
|
|
||||||
this.helmet = ItemStack.getAirItem();
|
this.helmet = ItemStack.AIR;
|
||||||
this.chestplate = ItemStack.getAirItem();
|
this.chestplate = ItemStack.AIR;
|
||||||
this.leggings = ItemStack.getAirItem();
|
this.leggings = ItemStack.AIR;
|
||||||
this.boots = ItemStack.getAirItem();
|
this.boots = ItemStack.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -343,7 +343,7 @@ public class Metadata {
|
|||||||
case TYPE_OPTCHAT:
|
case TYPE_OPTCHAT:
|
||||||
return (Value<T>) OptChat((Component) null);
|
return (Value<T>) OptChat((Component) null);
|
||||||
case TYPE_SLOT:
|
case TYPE_SLOT:
|
||||||
return (Value<T>) Slot(ItemStack.getAirItem());
|
return (Value<T>) Slot(ItemStack.AIR);
|
||||||
case TYPE_BOOLEAN:
|
case TYPE_BOOLEAN:
|
||||||
return (Value<T>) Boolean(false);
|
return (Value<T>) Boolean(false);
|
||||||
case TYPE_ROTATION:
|
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.chunk.ChunkUtils;
|
||||||
import net.minestom.server.utils.entity.EntityUtils;
|
import net.minestom.server.utils.entity.EntityUtils;
|
||||||
import net.minestom.server.utils.instance.InstanceUtils;
|
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.Cooldown;
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
import net.minestom.server.utils.time.UpdateOption;
|
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
|
// Clear all viewable entities
|
||||||
this.viewableEntities.forEach(entity -> entity.removeViewer(this));
|
this.viewableEntities.forEach(entity -> entity.removeViewer(this));
|
||||||
// Clear all viewable chunks
|
// Clear all viewable chunks
|
||||||
@ -1096,42 +1089,16 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
MinecraftServer.getBossBarManager().removeBossBar(this, bar);
|
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
|
@Override
|
||||||
public void openBook(@NotNull Book book) {
|
public void openBook(@NotNull Book book) {
|
||||||
// make the book
|
ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK)
|
||||||
ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
|
.meta(WrittenBookMeta.fromAdventure(book, this))
|
||||||
writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book, this));
|
.build();
|
||||||
|
|
||||||
// Set book in offhand
|
// Set book in offhand
|
||||||
SetSlotPacket setBookPacket = new SetSlotPacket();
|
SetSlotPacket setBookPacket = new SetSlotPacket();
|
||||||
setBookPacket.windowId = 0;
|
setBookPacket.windowId = 0;
|
||||||
setBookPacket.slot = 45;
|
setBookPacket.slot = PlayerInventoryUtils.OFFHAND_SLOT;
|
||||||
setBookPacket.itemStack = writtenBook;
|
setBookPacket.itemStack = writtenBook;
|
||||||
playerConnection.sendPacket(setBookPacket);
|
playerConnection.sendPacket(setBookPacket);
|
||||||
|
|
||||||
@ -1143,7 +1110,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
// Restore the item in offhand
|
// Restore the item in offhand
|
||||||
SetSlotPacket restoreItemPacket = new SetSlotPacket();
|
SetSlotPacket restoreItemPacket = new SetSlotPacket();
|
||||||
restoreItemPacket.windowId = 0;
|
restoreItemPacket.windowId = 0;
|
||||||
restoreItemPacket.slot = 45;
|
restoreItemPacket.slot = PlayerInventoryUtils.OFFHAND_SLOT;
|
||||||
restoreItemPacket.itemStack = getItemInOffHand();
|
restoreItemPacket.itemStack = getItemInOffHand();
|
||||||
playerConnection.sendPacket(restoreItemPacket);
|
playerConnection.sendPacket(restoreItemPacket);
|
||||||
}
|
}
|
||||||
@ -1996,10 +1963,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
ItemStack cursorItem;
|
ItemStack cursorItem;
|
||||||
if (openInventory == null) {
|
if (openInventory == null) {
|
||||||
cursorItem = getInventory().getCursorItem();
|
cursorItem = getInventory().getCursorItem();
|
||||||
getInventory().setCursorItem(ItemStack.getAirItem());
|
getInventory().setCursorItem(ItemStack.AIR);
|
||||||
} else {
|
} else {
|
||||||
cursorItem = openInventory.getCursorItem(this);
|
cursorItem = openInventory.getCursorItem(this);
|
||||||
openInventory.setCursorItem(this, ItemStack.getAirItem());
|
openInventory.setCursorItem(this, ItemStack.AIR);
|
||||||
}
|
}
|
||||||
if (!cursorItem.isAir()) {
|
if (!cursorItem.isAir()) {
|
||||||
// Add item to inventory if he hasn't been able to drop it
|
// 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.entity.Player;
|
||||||
import net.minestom.server.instance.block.BlockFace;
|
import net.minestom.server.instance.block.BlockFace;
|
||||||
import net.minestom.server.inventory.Inventory;
|
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.inventory.PlayerInventory;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
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) {
|
public void clickWindow(boolean playerInventory, short slot, byte button, short action, int mode) {
|
||||||
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
|
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
|
||||||
InventoryModifier inventoryModifier = inventory == null ? fakePlayer.getInventory() : inventory;
|
AbstractInventory abstractInventory = inventory == null ? fakePlayer.getInventory() : inventory;
|
||||||
playerInventory = inventoryModifier instanceof PlayerInventory;
|
playerInventory = abstractInventory instanceof PlayerInventory;
|
||||||
|
|
||||||
slot = playerInventory ? (short) PlayerInventoryUtils.convertToPacketSlot(slot) : slot;
|
slot = playerInventory ? (short) PlayerInventoryUtils.convertToPacketSlot(slot) : slot;
|
||||||
|
|
||||||
ItemStack itemStack = inventoryModifier.getItemStack(slot);
|
ItemStack itemStack = abstractInventory.getItemStack(slot);
|
||||||
|
|
||||||
ClientClickWindowPacket clickWindowPacket = new ClientClickWindowPacket();
|
ClientClickWindowPacket clickWindowPacket = new ClientClickWindowPacket();
|
||||||
clickWindowPacket.windowId = playerInventory ? 0 : inventory.getWindowId();
|
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) {
|
protected ItemContainingMeta(@NotNull Entity entity, @NotNull Metadata metadata, @NotNull Material defaultItemMaterial) {
|
||||||
super(entity, metadata);
|
super(entity, metadata);
|
||||||
this.defaultItem = new ItemStack(defaultItemMaterial, (byte) 1);
|
this.defaultItem = ItemStack.of(defaultItemMaterial);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -18,7 +18,7 @@ public class FireworkRocketMeta extends EntityMeta implements ProjectileMeta {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public ItemStack getFireworkInfo() {
|
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) {
|
public void setFireworkInfo(@NotNull ItemStack value) {
|
||||||
|
@ -19,7 +19,7 @@ public class ItemFrameMeta extends EntityMeta implements ObjectDataProvider {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public ItemStack getItem() {
|
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) {
|
public void setItem(@NotNull ItemStack value) {
|
||||||
|
@ -41,13 +41,13 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
setLeftLegRotation(new Vector(-1f, 0, -1f));
|
setLeftLegRotation(new Vector(-1f, 0, -1f));
|
||||||
setRightLegRotation(new Vector(1, 0, 1));
|
setRightLegRotation(new Vector(1, 0, 1));
|
||||||
|
|
||||||
this.mainHandItem = ItemStack.getAirItem();
|
this.mainHandItem = ItemStack.AIR;
|
||||||
this.offHandItem = ItemStack.getAirItem();
|
this.offHandItem = ItemStack.AIR;
|
||||||
|
|
||||||
this.helmet = ItemStack.getAirItem();
|
this.helmet = ItemStack.AIR;
|
||||||
this.chestplate = ItemStack.getAirItem();
|
this.chestplate = ItemStack.AIR;
|
||||||
this.leggings = ItemStack.getAirItem();
|
this.leggings = ItemStack.AIR;
|
||||||
this.boots = ItemStack.getAirItem();
|
this.boots = ItemStack.AIR;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
// FIXME: https://wiki.vg/Object_Data#Item_Frame_.28id_71.29
|
// 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."
|
// "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.
|
* @deprecated Use {@link net.minestom.server.entity.metadata.other.ItemFrameMeta} instead.
|
||||||
*/
|
*/
|
||||||
@ -37,7 +38,7 @@ public class EntityItemFrame extends ObjectEntity {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public ItemStack getItemStack() {
|
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
|
* @return the item
|
||||||
*/
|
*/
|
||||||
public ItemStack getItemStack() {
|
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
|
@NotNull
|
||||||
public ItemStack getPotion() {
|
public ItemStack getPotion() {
|
||||||
return metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
return metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPotion(@NotNull ItemStack potion) {
|
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
|
@Override
|
||||||
public ItemStack apply(ItemStack stack, Data data) {
|
public ItemStack apply(ItemStack stack, Data data) {
|
||||||
for (Condition c : conditions) {
|
for (Condition c : conditions) {
|
||||||
if(!c.test(data))
|
if (!c.test(data))
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
return baseFunction.apply(stack, data);
|
return baseFunction.apply(stack, data);
|
||||||
|
@ -31,10 +31,10 @@ public class LootTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ItemStack> generate(Data arguments) {
|
public List<ItemStack> generate(Data arguments) {
|
||||||
if(arguments == null)
|
if (arguments == null)
|
||||||
arguments = Data.EMPTY;
|
arguments = Data.EMPTY;
|
||||||
List<ItemStack> output = new LinkedList<>();
|
List<ItemStack> output = new LinkedList<>();
|
||||||
for(Pool p : pools) {
|
for (Pool p : pools) {
|
||||||
p.generate(output, arguments);
|
p.generate(output, arguments);
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@ -74,18 +74,18 @@ public class LootTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void generate(List<ItemStack> output, Data arguments) {
|
public void generate(List<ItemStack> output, Data arguments) {
|
||||||
for(Condition c : conditions) {
|
for (Condition c : conditions) {
|
||||||
if(!c.test(arguments))
|
if (!c.test(arguments))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Random rng = new Random();
|
Random rng = new Random();
|
||||||
int luck = arguments.getOrDefault(LUCK_KEY, 0);
|
int luck = arguments.getOrDefault(LUCK_KEY, 0);
|
||||||
int rollCount = rng.nextInt(maxRollCount - minRollCount +1 /*inclusive*/) + minRollCount;
|
int rollCount = rng.nextInt(maxRollCount - minRollCount + 1 /*inclusive*/) + minRollCount;
|
||||||
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount +1 /*inclusive*/) + bonusMinRollCount;
|
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount + 1 /*inclusive*/) + bonusMinRollCount;
|
||||||
bonusRollCount *= luck;
|
bonusRollCount *= luck;
|
||||||
// TODO: implement luck (quality/weight) weight=floor( weight + (quality * generic.luck))
|
// TODO: implement luck (quality/weight) weight=floor( weight + (quality * generic.luck))
|
||||||
WeightedRandom<Entry> weightedRandom = new WeightedRandom<>(entries);
|
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 entry = weightedRandom.get(rng);
|
||||||
entry.generateStacks(output, arguments);
|
entry.generateStacks(output, arguments);
|
||||||
}
|
}
|
||||||
@ -122,8 +122,8 @@ public class LootTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void generateStacks(List<ItemStack> output, Data arguments) {
|
public final void generateStacks(List<ItemStack> output, Data arguments) {
|
||||||
for(Condition c : conditions) {
|
for (Condition c : conditions) {
|
||||||
if(!c.test(arguments))
|
if (!c.test(arguments))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
generate(output, arguments);
|
generate(output, arguments);
|
||||||
|
@ -17,11 +17,11 @@ public class AlternativesEntry extends LootTable.Entry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(List<ItemStack> output, Data arguments) {
|
public void generate(List<ItemStack> output, Data arguments) {
|
||||||
for(LootTable.Entry c : children) {
|
for (LootTable.Entry c : children) {
|
||||||
int previousSize = output.size();
|
int previousSize = output.size();
|
||||||
c.generateStacks(output, arguments);
|
c.generateStacks(output, arguments);
|
||||||
int newSize = output.size();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@ public class ItemEntry extends LootTable.Entry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(List<ItemStack> output, Data arguments) {
|
public void generate(List<ItemStack> output, Data arguments) {
|
||||||
ItemStack stack = new ItemStack(item, (byte)1);
|
ItemStack stack = ItemStack.of(item);
|
||||||
for (LootTableFunction function : functions) {
|
for (LootTableFunction function : functions) {
|
||||||
stack = function.apply(stack, arguments);
|
stack = function.apply(stack, arguments);
|
||||||
}
|
}
|
||||||
if(!stack.isAir()) {
|
if (!stack.isAir()) {
|
||||||
output.add(stack);
|
output.add(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,11 @@ public class SequenceEntry extends LootTable.Entry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(List<ItemStack> output, Data arguments) {
|
public void generate(List<ItemStack> output, Data arguments) {
|
||||||
for(LootTable.Entry c : children) {
|
for (LootTable.Entry c : children) {
|
||||||
int previousSize = output.size();
|
int previousSize = output.size();
|
||||||
c.generateStacks(output, arguments);
|
c.generateStacks(output, arguments);
|
||||||
int newSize = output.size();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,19 @@ public class TagEntry extends LootTable.Entry {
|
|||||||
@Override
|
@Override
|
||||||
public void generate(List<ItemStack> output, Data arguments) {
|
public void generate(List<ItemStack> output, Data arguments) {
|
||||||
Set<NamespaceID> values = tag.getValues();
|
Set<NamespaceID> values = tag.getValues();
|
||||||
if(values.isEmpty())
|
if (values.isEmpty())
|
||||||
return;
|
return;
|
||||||
Material[] asArrayOfItems = new Material[values.size()];
|
Material[] asArrayOfItems = new Material[values.size()];
|
||||||
int ptr = 0;
|
int ptr = 0;
|
||||||
for (NamespaceID id : values) {
|
for (NamespaceID id : values) {
|
||||||
asArrayOfItems[ptr++] = Registries.getMaterial(id);
|
asArrayOfItems[ptr++] = Registries.getMaterial(id);
|
||||||
}
|
}
|
||||||
if(expand) {
|
if (expand) {
|
||||||
Material selectedItem = asArrayOfItems[rng.nextInt(asArrayOfItems.length)];
|
Material selectedMaterial = asArrayOfItems[rng.nextInt(asArrayOfItems.length)];
|
||||||
output.add(new ItemStack(selectedItem, (byte) 1));
|
output.add(ItemStack.of(selectedMaterial));
|
||||||
} else {
|
} else {
|
||||||
for(Material item : asArrayOfItems) {
|
for (Material material : asArrayOfItems) {
|
||||||
output.add(new ItemStack(item, (byte) 1));
|
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;
|
package net.minestom.server.inventory;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.Viewable;
|
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.entity.Player;
|
||||||
import net.minestom.server.inventory.click.ClickType;
|
import net.minestom.server.inventory.click.ClickType;
|
||||||
import net.minestom.server.inventory.click.InventoryClickLoopHandler;
|
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.click.InventoryClickResult;
|
||||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
|
||||||
import net.minestom.server.item.ItemStack;
|
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.OpenWindowPacket;
|
||||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
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.WindowItemsPacket;
|
||||||
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
|
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
|
||||||
import net.minestom.server.network.player.PlayerConnection;
|
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.inventory.PlayerInventoryUtils;
|
||||||
import net.minestom.server.utils.validate.Check;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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.
|
* 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)}.
|
* 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)
|
// incremented each time an inventory is created (used in the window packets)
|
||||||
private static final AtomicInteger LAST_INVENTORY_ID = new AtomicInteger();
|
private static final AtomicInteger LAST_INVENTORY_ID = new AtomicInteger();
|
||||||
@ -46,41 +36,32 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
private final byte id;
|
private final byte id;
|
||||||
// the type of this inventory
|
// the type of this inventory
|
||||||
private final InventoryType inventoryType;
|
private final InventoryType inventoryType;
|
||||||
// the title of this inventory)
|
// the title of this inventory
|
||||||
private String title;
|
private Component title;
|
||||||
|
|
||||||
// the size based on the inventory type
|
|
||||||
private final int size;
|
|
||||||
|
|
||||||
private final int offset;
|
private final int offset;
|
||||||
|
|
||||||
// the items in this inventory
|
|
||||||
private final ItemStack[] itemStacks;
|
|
||||||
// the players currently viewing this inventory
|
// the players currently viewing this inventory
|
||||||
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||||
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
||||||
// (player -> cursor item) map, used by the click listeners
|
// (player -> cursor item) map, used by the click listeners
|
||||||
private final ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// list of conditions/callbacks assigned to this inventory
|
public Inventory(@NotNull InventoryType inventoryType, @NotNull Component title) {
|
||||||
private final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
|
super(inventoryType.getSize());
|
||||||
// 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) {
|
|
||||||
this.id = generateId();
|
this.id = generateId();
|
||||||
this.inventoryType = inventoryType;
|
this.inventoryType = inventoryType;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
this.size = inventoryType.getSize();
|
this.offset = getSize();
|
||||||
|
}
|
||||||
|
|
||||||
this.offset = size;
|
/**
|
||||||
|
* @deprecated use {@link Inventory#Inventory(InventoryType, Component)}
|
||||||
this.itemStacks = new ItemStack[size];
|
*/
|
||||||
|
@Deprecated
|
||||||
ArrayUtils.fill(itemStacks, ItemStack::getAirItem);
|
public Inventory(@NotNull InventoryType inventoryType, @NotNull String title) {
|
||||||
|
this(inventoryType, Component.text(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte generateId() {
|
private static byte generateId() {
|
||||||
@ -106,7 +87,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
* @return the inventory title
|
* @return the inventory title
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getTitle() {
|
public Component getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +96,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
*
|
*
|
||||||
* @param title the new inventory title
|
* @param title the new inventory title
|
||||||
*/
|
*/
|
||||||
public void setTitle(@NotNull String title) {
|
public void setTitle(@NotNull Component title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
OpenWindowPacket packet = new OpenWindowPacket(title);
|
OpenWindowPacket packet = new OpenWindowPacket(title);
|
||||||
@ -141,85 +122,17 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
public synchronized void clear() {
|
||||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
super.clear();
|
||||||
inventoryType.toString() + " does not have slot " + slot);
|
// Clear cursor
|
||||||
|
getViewers().forEach(player ->
|
||||||
safeItemInsert(slot, itemStack);
|
setCursorItem(player, ItemStack.AIR));
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the inventory for all viewers.
|
* Refreshes the inventory for all viewers.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
sendPacketToViewers(createNewWindowItemsPacket());
|
sendPacketToViewers(createNewWindowItemsPacket());
|
||||||
}
|
}
|
||||||
@ -239,15 +152,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
playerConnection.sendPacket(createNewWindowItemsPacket());
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<Player> getViewers() {
|
public Set<Player> getViewers() {
|
||||||
@ -289,7 +193,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public ItemStack getCursorItem(@NotNull Player player) {
|
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 slot the internal slot id
|
||||||
* @param itemStack the item to insert
|
* @param itemStack the item to insert
|
||||||
*/
|
*/
|
||||||
private synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
@Override
|
||||||
setItemStackInternal(slot, itemStack);
|
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||||
|
this.itemStacks[slot] = itemStack;
|
||||||
SetSlotPacket setSlotPacket = new SetSlotPacket();
|
SetSlotPacket setSlotPacket = new SetSlotPacket();
|
||||||
setSlotPacket.windowId = getWindowId();
|
setSlotPacket.windowId = getWindowId();
|
||||||
setSlotPacket.slot = (short) slot;
|
setSlotPacket.slot = (short) slot;
|
||||||
@ -332,19 +237,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
sendPacketToViewers(setSlotPacket);
|
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}.
|
* 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 player the player to change the cursor item
|
||||||
* @param itemStack 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);
|
this.cursorPlayersItem.put(player, itemStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +300,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
} else {
|
} else {
|
||||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||||
}
|
}
|
||||||
setCursorPlayerItem(player, clickResult.getCursor());
|
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||||
|
|
||||||
if (!clickResult.isCancel())
|
if (!clickResult.isCancel())
|
||||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||||
@ -435,7 +327,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
} else {
|
} else {
|
||||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||||
}
|
}
|
||||||
setCursorPlayerItem(player, clickResult.getCursor());
|
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||||
|
|
||||||
if (!clickResult.isCancel())
|
if (!clickResult.isCancel())
|
||||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
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);
|
updateFromClick(clickResult, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursorPlayerItem(player, clickResult.getCursor());
|
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||||
playerInventory.update();
|
playerInventory.update();
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@ -543,7 +435,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
final boolean outsideDrop = slot == -999;
|
final boolean outsideDrop = slot == -999;
|
||||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||||
final ItemStack clicked = outsideDrop ?
|
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 ItemStack cursor = getCursorItem(player);
|
||||||
|
|
||||||
final InventoryClickResult clickResult = clickProcessor.drop(isInWindow ? this : null, 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();
|
return !clickResult.isCancel();
|
||||||
}
|
}
|
||||||
@ -574,7 +466,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||||
final ItemStack clicked = slot != -999 ?
|
final ItemStack clicked = slot != -999 ?
|
||||||
(isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)) :
|
(isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)) :
|
||||||
ItemStack.getAirItem();
|
ItemStack.AIR;
|
||||||
final ItemStack cursor = getCursorItem(player);
|
final ItemStack cursor = getCursorItem(player);
|
||||||
|
|
||||||
final InventoryClickResult clickResult = clickProcessor.dragging(isInWindow ? this : null, player,
|
final InventoryClickResult clickResult = clickProcessor.dragging(isInWindow ? this : null, player,
|
||||||
@ -600,7 +492,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
updateFromClick(clickResult, player);
|
updateFromClick(clickResult, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursorPlayerItem(player, clickResult.getCursor());
|
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||||
|
|
||||||
return !clickResult.isCancel();
|
return !clickResult.isCancel();
|
||||||
}
|
}
|
||||||
@ -629,27 +521,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
|||||||
if (clickResult.doRefresh())
|
if (clickResult.doRefresh())
|
||||||
updateFromClick(clickResult, player);
|
updateFromClick(clickResult, player);
|
||||||
|
|
||||||
setCursorPlayerItem(player, clickResult.getCursor());
|
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||||
|
|
||||||
return !clickResult.isCancel();
|
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
|
* 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);
|
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;
|
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.entity.Player;
|
||||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
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.ClickType;
|
||||||
import net.minestom.server.inventory.click.InventoryClickLoopHandler;
|
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.click.InventoryClickResult;
|
||||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||||
import net.minestom.server.item.ItemStack;
|
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.EntityEquipmentPacket;
|
||||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
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.WindowItemsPacket;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.*;
|
import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the inventory of a {@link Player}, retrieved with {@link Player#getInventory()}.
|
* 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 INVENTORY_SIZE = 46;
|
||||||
|
public static final int INNER_INVENTORY_SIZE = 36;
|
||||||
|
|
||||||
protected final Player player;
|
protected final Player player;
|
||||||
protected final ItemStack[] items = new ItemStack[INVENTORY_SIZE];
|
private ItemStack cursorItem = ItemStack.AIR;
|
||||||
private ItemStack cursorItem = ItemStack.getAirItem();
|
|
||||||
|
|
||||||
private final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
|
|
||||||
private final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
|
|
||||||
|
|
||||||
private Data data;
|
|
||||||
|
|
||||||
public PlayerInventory(@NotNull Player player) {
|
public PlayerInventory(@NotNull Player player) {
|
||||||
|
super(INVENTORY_SIZE);
|
||||||
this.player = player;
|
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
|
@Override
|
||||||
public void addInventoryCondition(@NotNull InventoryCondition inventoryCondition) {
|
public void addInventoryCondition(@NotNull InventoryCondition inventoryCondition) {
|
||||||
|
// fix packet slot to inventory slot conversion
|
||||||
InventoryCondition condition = (p, slot, clickType, inventoryConditionResult) -> {
|
InventoryCondition condition = (p, slot, clickType, inventoryConditionResult) -> {
|
||||||
final int convertedSlot = convertPlayerInventorySlot(slot, OFFSET);
|
final int convertedSlot = convertPlayerInventorySlot(slot, OFFSET);
|
||||||
inventoryCondition.accept(p, convertedSlot, clickType, inventoryConditionResult);
|
inventoryCondition.accept(p, convertedSlot, clickType, inventoryConditionResult);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.inventoryConditions.add(condition);
|
super.addInventoryCondition(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
public synchronized void clear() {
|
||||||
PlayerSetItemStackEvent setItemStackEvent = new PlayerSetItemStackEvent(player, slot, itemStack);
|
super.clear();
|
||||||
player.callEvent(PlayerSetItemStackEvent.class, setItemStackEvent);
|
// Reset cursor
|
||||||
if (setItemStackEvent.isCancelled())
|
setCursorItem(ItemStack.AIR);
|
||||||
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();
|
|
||||||
|
|
||||||
// Update equipments
|
// Update equipments
|
||||||
this.player.sendPacketToViewersAndSelf(player.getEquipmentsPacket());
|
this.player.sendPacketToViewersAndSelf(player.getEquipmentsPacket());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getInnerSize() {
|
||||||
return INVENTORY_SIZE;
|
return INNER_INVENTORY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -214,20 +127,11 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
* Refreshes the player inventory by sending a {@link WindowItemsPacket} containing all.
|
* Refreshes the player inventory by sending a {@link WindowItemsPacket} containing all.
|
||||||
* the inventory items
|
* the inventory items
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
player.getPlayerConnection().sendPacket(createWindowItemsPacket());
|
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.
|
* 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 IllegalArgumentException if the slot {@code slot} does not exist
|
||||||
* @throws NullPointerException if {@code itemStack} is null
|
* @throws NullPointerException if {@code itemStack} is null
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||||
"The slot " + slot + " does not exist for player");
|
"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
|
// Sync equipment
|
||||||
if (equipmentSlot != null) {
|
if (equipmentSlot != null) {
|
||||||
@ -308,10 +213,6 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
//refreshSlot((short) slot);
|
//refreshSlot((short) slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setItemStackInternal(int slot, ItemStack itemStack) {
|
|
||||||
items[slot] = itemStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an item from a packet slot.
|
* 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 offset offset (generally 9 to ignore armor and craft slots)
|
||||||
* @param itemStack the item stack to set
|
* @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);
|
final int convertedSlot = convertPlayerInventorySlot(slot, offset);
|
||||||
setItemStack(convertedSlot, itemStack);
|
setItemStack(convertedSlot, itemStack);
|
||||||
}
|
}
|
||||||
@ -333,7 +234,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
*/
|
*/
|
||||||
protected ItemStack getItemStack(int slot, int offset) {
|
protected ItemStack getItemStack(int slot, int offset) {
|
||||||
final int convertedSlot = convertPlayerInventorySlot(slot, 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() {
|
private WindowItemsPacket createWindowItemsPacket() {
|
||||||
ItemStack[] convertedSlots = new ItemStack[INVENTORY_SIZE];
|
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);
|
final int slot = convertToPacketSlot(i);
|
||||||
convertedSlots[slot] = items[i];
|
convertedSlots[slot] = itemStacks[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowItemsPacket windowItemsPacket = new WindowItemsPacket();
|
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) {
|
public boolean drop(@NotNull Player player, int mode, int slot, int button) {
|
||||||
final ItemStack cursor = getCursorItem();
|
final ItemStack cursor = getCursorItem();
|
||||||
final boolean outsideDrop = slot == -999;
|
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,
|
final InventoryClickResult clickResult = clickProcessor.drop(null, player,
|
||||||
mode, slot, button, clicked, cursor);
|
mode, slot, button, clicked, cursor);
|
||||||
@ -441,7 +342,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
|
|
||||||
final boolean hotBarClick = convertToPacketSlot(slot) < 9;
|
final boolean hotBarClick = convertToPacketSlot(slot) < 9;
|
||||||
final InventoryClickResult clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
|
final InventoryClickResult clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
|
||||||
new InventoryClickLoopHandler(0, items.length, 1,
|
new InventoryClickLoopHandler(0, itemStacks.length, 1,
|
||||||
i -> {
|
i -> {
|
||||||
if (hotBarClick) {
|
if (hotBarClick) {
|
||||||
return i < 9 ? i + 9 : i - 9;
|
return i < 9 ? i + 9 : i - 9;
|
||||||
@ -492,7 +393,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
@Override
|
@Override
|
||||||
public boolean dragging(@NotNull Player player, int slot, int button) {
|
public boolean dragging(@NotNull Player player, int slot, int button) {
|
||||||
final ItemStack cursor = getCursorItem();
|
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,
|
final InventoryClickResult clickResult = clickProcessor.dragging(null, player,
|
||||||
slot, button,
|
slot, button,
|
||||||
@ -516,9 +417,9 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
final ItemStack cursor = getCursorItem();
|
final ItemStack cursor = getCursorItem();
|
||||||
|
|
||||||
final InventoryClickResult clickResult = clickProcessor.doubleClick(null, player, slot, cursor,
|
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,
|
i -> i < 9 ? i + 9 : i - 9,
|
||||||
index -> items[index],
|
index -> itemStacks[index],
|
||||||
this::setItemStack));
|
this::setItemStack));
|
||||||
|
|
||||||
if (clickResult == null)
|
if (clickResult == null)
|
||||||
@ -531,15 +432,4 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
|||||||
|
|
||||||
return !clickResult.isCancel();
|
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;
|
ItemStack resultClicked;
|
||||||
|
|
||||||
if (clickedRule.canBeStacked(clicked, cursor)) {
|
if (clickedRule.canBeStacked(clicked, cursor)) {
|
||||||
final int amount = clicked.getAmount() + 1;
|
final int amount = clickedRule.getAmount(clicked) + 1;
|
||||||
if (!clickedRule.canApply(clicked, amount)) {
|
if (!clickedRule.canApply(clicked, amount)) {
|
||||||
return clickResult;
|
return clickResult;
|
||||||
} else {
|
} else {
|
||||||
resultCursor = cursorRule.apply(cursor, cursorRule.getAmount(cursor) - 1);
|
resultCursor = cursorRule.apply(cursor, operand -> operand - 1);
|
||||||
resultClicked = clickedRule.apply(clicked, amount);
|
resultClicked = clickedRule.apply(clicked, amount);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cursor.isAir()) {
|
if (cursor.isAir()) {
|
||||||
final int amount = (int) Math.ceil((double) clicked.getAmount() / 2d);
|
final int amount = (int) Math.ceil((double) clickedRule.getAmount(clicked) / 2d);
|
||||||
resultCursor = clicked.clone();
|
resultCursor = cursorRule.apply(clicked, amount);
|
||||||
resultCursor = cursorRule.apply(resultCursor, amount);
|
resultClicked = clickedRule.apply(clicked, operand -> operand / 2);
|
||||||
|
|
||||||
resultClicked = clicked.clone();
|
|
||||||
resultClicked = clickedRule.apply(resultClicked, clicked.getAmount() / 2);
|
|
||||||
} else {
|
} else {
|
||||||
if (clicked.isAir()) {
|
if (clicked.isAir()) {
|
||||||
final int amount = cursor.getAmount();
|
resultCursor = cursorRule.apply(cursor, operand -> operand - 1);
|
||||||
resultCursor = cursor.clone();
|
resultClicked = clickedRule.apply(cursor, 1);
|
||||||
resultCursor = cursorRule.apply(resultCursor, amount - 1);
|
|
||||||
|
|
||||||
resultClicked = cursor.clone();
|
|
||||||
resultClicked = clickedRule.apply(resultClicked, 1);
|
|
||||||
} else {
|
} else {
|
||||||
resultCursor = clicked;
|
resultCursor = clicked;
|
||||||
resultClicked = cursor;
|
resultClicked = cursor;
|
||||||
@ -155,11 +148,11 @@ public class InventoryClickProcessor {
|
|||||||
if (clicked.isAir()) {
|
if (clicked.isAir()) {
|
||||||
// Set held item [key] to slot
|
// Set held item [key] to slot
|
||||||
resultClicked = cursor;
|
resultClicked = cursor;
|
||||||
resultHeld = ItemStack.getAirItem();
|
resultHeld = ItemStack.AIR;
|
||||||
} else {
|
} else {
|
||||||
if (cursor.isAir()) {
|
if (cursor.isAir()) {
|
||||||
// if held item [key] is air then set clicked to held
|
// if held item [key] is air then set clicked to held
|
||||||
resultClicked = ItemStack.getAirItem();
|
resultClicked = ItemStack.AIR;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise replace held item and held
|
// Otherwise replace held item and held
|
||||||
resultClicked = cursor;
|
resultClicked = cursor;
|
||||||
@ -189,7 +182,7 @@ public class InventoryClickProcessor {
|
|||||||
final StackingRule clickedRule = clicked.getStackingRule();
|
final StackingRule clickedRule = clicked.getStackingRule();
|
||||||
|
|
||||||
boolean filled = false;
|
boolean filled = false;
|
||||||
ItemStack resultClicked = clicked.clone();
|
ItemStack resultClicked = clicked;
|
||||||
|
|
||||||
for (InventoryClickLoopHandler loopHandler : loopHandlers) {
|
for (InventoryClickLoopHandler loopHandler : loopHandlers) {
|
||||||
final Int2IntFunction indexModifier = loopHandler.getIndexModifier();
|
final Int2IntFunction indexModifier = loopHandler.getIndexModifier();
|
||||||
@ -242,7 +235,7 @@ public class InventoryClickProcessor {
|
|||||||
|
|
||||||
// Switch
|
// Switch
|
||||||
itemSetter.accept(index, resultClicked);
|
itemSetter.accept(index, resultClicked);
|
||||||
itemSetter.accept(slot, ItemStack.getAirItem());
|
itemSetter.accept(slot, ItemStack.AIR);
|
||||||
filled = true;
|
filled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -287,16 +280,16 @@ public class InventoryClickProcessor {
|
|||||||
int finalCursorAmount = cursorAmount;
|
int finalCursorAmount = cursorAmount;
|
||||||
|
|
||||||
for (int s : slots) {
|
for (int s : slots) {
|
||||||
final ItemStack draggedItem = cursor.clone();
|
|
||||||
ItemStack slotItem = itemGetter.apply(s);
|
ItemStack slotItem = itemGetter.apply(s);
|
||||||
|
|
||||||
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
||||||
if (clickResult.isCancel())
|
if (clickResult.isCancel())
|
||||||
break;
|
break;
|
||||||
|
StackingRule slotItemRule = slotItem.getStackingRule();
|
||||||
|
|
||||||
final int maxSize = stackingRule.getMaxSize();
|
final int maxSize = stackingRule.getMaxSize();
|
||||||
if (stackingRule.canBeStacked(draggedItem, slotItem)) {
|
if (stackingRule.canBeStacked(cursor, slotItem)) {
|
||||||
final int amount = slotItem.getAmount() + slotSize;
|
final int amount = slotItemRule.getAmount(slotItem) + slotSize;
|
||||||
if (stackingRule.canApply(slotItem, amount)) {
|
if (stackingRule.canApply(slotItem, amount)) {
|
||||||
slotItem = stackingRule.apply(slotItem, amount);
|
slotItem = stackingRule.apply(slotItem, amount);
|
||||||
finalCursorAmount -= slotSize;
|
finalCursorAmount -= slotSize;
|
||||||
@ -306,7 +299,7 @@ public class InventoryClickProcessor {
|
|||||||
finalCursorAmount -= removedAmount;
|
finalCursorAmount -= removedAmount;
|
||||||
}
|
}
|
||||||
} else if (slotItem.isAir()) {
|
} else if (slotItem.isAir()) {
|
||||||
slotItem = stackingRule.apply(draggedItem, slotSize);
|
slotItem = stackingRule.apply(cursor, slotSize);
|
||||||
finalCursorAmount -= slotSize;
|
finalCursorAmount -= slotSize;
|
||||||
}
|
}
|
||||||
itemSetter.accept(s, slotItem);
|
itemSetter.accept(s, slotItem);
|
||||||
@ -327,15 +320,16 @@ public class InventoryClickProcessor {
|
|||||||
if (size > cursorAmount)
|
if (size > cursorAmount)
|
||||||
return null;
|
return null;
|
||||||
for (int s : slots) {
|
for (int s : slots) {
|
||||||
ItemStack draggedItem = cursor.clone();
|
ItemStack draggedItem = cursor;
|
||||||
ItemStack slotItem = itemGetter.apply(s);
|
ItemStack slotItem = itemGetter.apply(s);
|
||||||
|
|
||||||
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
||||||
if (clickResult.isCancel())
|
if (clickResult.isCancel())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
StackingRule slotItemRule = slotItem.getStackingRule();
|
||||||
if (stackingRule.canBeStacked(draggedItem, slotItem)) {
|
if (stackingRule.canBeStacked(draggedItem, slotItem)) {
|
||||||
final int amount = slotItem.getAmount() + 1;
|
final int amount = slotItemRule.getAmount(slotItem) + 1;
|
||||||
if (stackingRule.canApply(slotItem, amount)) {
|
if (stackingRule.canApply(slotItem, amount)) {
|
||||||
slotItem = stackingRule.apply(slotItem, amount);
|
slotItem = stackingRule.apply(slotItem, amount);
|
||||||
itemSetter.accept(s, slotItem);
|
itemSetter.accept(s, slotItem);
|
||||||
@ -377,7 +371,7 @@ public class InventoryClickProcessor {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public InventoryClickResult doubleClick(@Nullable Inventory inventory, @NotNull Player player, int slot,
|
public InventoryClickResult doubleClick(@Nullable Inventory inventory, @NotNull Player player, int slot,
|
||||||
@NotNull ItemStack cursor, @NotNull InventoryClickLoopHandler... loopHandlers) {
|
@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()) {
|
if (clickResult.isCancel()) {
|
||||||
return clickResult;
|
return clickResult;
|
||||||
@ -446,8 +440,8 @@ public class InventoryClickProcessor {
|
|||||||
final StackingRule clickedRule = clicked.getStackingRule();
|
final StackingRule clickedRule = clicked.getStackingRule();
|
||||||
final StackingRule cursorRule = cursor.getStackingRule();
|
final StackingRule cursorRule = cursor.getStackingRule();
|
||||||
|
|
||||||
ItemStack resultClicked = clicked.clone();
|
ItemStack resultClicked = clicked;
|
||||||
ItemStack resultCursor = cursor.clone();
|
ItemStack resultCursor = cursor;
|
||||||
|
|
||||||
|
|
||||||
if (slot == -999) {
|
if (slot == -999) {
|
||||||
@ -455,7 +449,7 @@ public class InventoryClickProcessor {
|
|||||||
if (button == 0) {
|
if (button == 0) {
|
||||||
// Left (drop all)
|
// Left (drop all)
|
||||||
final int amount = cursorRule.getAmount(resultCursor);
|
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);
|
final boolean dropResult = player.dropItem(dropItem);
|
||||||
clickResult.setCancel(!dropResult);
|
clickResult.setCancel(!dropResult);
|
||||||
if (dropResult) {
|
if (dropResult) {
|
||||||
@ -463,7 +457,7 @@ public class InventoryClickProcessor {
|
|||||||
}
|
}
|
||||||
} else if (button == 1) {
|
} else if (button == 1) {
|
||||||
// Right (drop 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);
|
final boolean dropResult = player.dropItem(dropItem);
|
||||||
clickResult.setCancel(!dropResult);
|
clickResult.setCancel(!dropResult);
|
||||||
if (dropResult) {
|
if (dropResult) {
|
||||||
@ -476,7 +470,7 @@ public class InventoryClickProcessor {
|
|||||||
} else if (mode == 4) {
|
} else if (mode == 4) {
|
||||||
if (button == 0) {
|
if (button == 0) {
|
||||||
// Drop key Q (drop 1)
|
// 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);
|
final boolean dropResult = player.dropItem(dropItem);
|
||||||
clickResult.setCancel(!dropResult);
|
clickResult.setCancel(!dropResult);
|
||||||
if (dropResult) {
|
if (dropResult) {
|
||||||
@ -487,7 +481,7 @@ public class InventoryClickProcessor {
|
|||||||
} else if (button == 1) {
|
} else if (button == 1) {
|
||||||
// Ctrl + Drop key Q (drop all)
|
// Ctrl + Drop key Q (drop all)
|
||||||
final int amount = cursorRule.getAmount(resultClicked);
|
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);
|
final boolean dropResult = player.dropItem(dropItem);
|
||||||
clickResult.setCancel(!dropResult);
|
clickResult.setCancel(!dropResult);
|
||||||
if (dropResult) {
|
if (dropResult) {
|
||||||
@ -517,7 +511,7 @@ public class InventoryClickProcessor {
|
|||||||
|
|
||||||
// Call ItemStack#onInventoryClick
|
// Call ItemStack#onInventoryClick
|
||||||
{
|
{
|
||||||
clickResult.getClicked().onInventoryClick(player, clickType, slot, isPlayerInventory);
|
//clickResult.getClicked().onInventoryClick(player, clickType, slot, isPlayerInventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the didCloseInventory field
|
// Reset the didCloseInventory field
|
||||||
@ -578,6 +572,7 @@ public class InventoryClickProcessor {
|
|||||||
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||||
InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor);
|
InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor);
|
||||||
player.callEvent(InventoryClickEvent.class, inventoryClickEvent);
|
player.callEvent(InventoryClickEvent.class, inventoryClickEvent);
|
||||||
|
System.out.println("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache(@NotNull Player player) {
|
public void clearCache(@NotNull Player player) {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package net.minestom.server.inventory.condition;
|
package net.minestom.server.inventory.condition;
|
||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
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;
|
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)}
|
* using {@link net.minestom.server.inventory.Inventory#addInventoryCondition(InventoryCondition)}
|
||||||
* or {@link net.minestom.server.inventory.PlayerInventory#addInventoryCondition(InventoryCondition)}
|
* or {@link net.minestom.server.inventory.PlayerInventory#addInventoryCondition(InventoryCondition)}
|
||||||
* in order to listen to any issued clicks.
|
* 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;
|
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_ENCHANTS(1),
|
||||||
HIDE_ATTRIBUTES(2),
|
HIDE_ATTRIBUTES(2),
|
||||||
HIDE_UNBREAKABLE(4),
|
HIDE_UNBREAKABLE(4),
|
||||||
@ -13,7 +13,7 @@ public enum ItemFlag {
|
|||||||
|
|
||||||
private final int bitFieldPart;
|
private final int bitFieldPart;
|
||||||
|
|
||||||
ItemFlag(int bit) {
|
ItemHideFlag(int bit) {
|
||||||
this.bitFieldPart = 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;
|
package net.minestom.server.item;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the stacking rule of an {@link ItemStack}.
|
* Represents the stacking rule of an {@link ItemStack}.
|
||||||
* This can be used to mimic the vanilla one (using the displayed item quantity)
|
* 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 item the {@link ItemStack} to applies the size to
|
||||||
* @param newAmount the new item size
|
* @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
|
@Contract("_, _ -> new")
|
||||||
public abstract ItemStack apply(@NotNull ItemStack item, int newAmount);
|
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}.
|
* Used to determine the current stack size of an {@link ItemStack}.
|
||||||
|
@ -1,103 +1,107 @@
|
|||||||
package net.minestom.server.item.metadata;
|
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.Position;
|
||||||
import net.minestom.server.utils.clone.CloneUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
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 final boolean lodestoneTracked;
|
||||||
private String lodestoneDimension;
|
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() {
|
public boolean isLodestoneTracked() {
|
||||||
return lodestoneTracked;
|
return lodestoneTracked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLodestoneTracked(boolean lodestoneTracked) {
|
public @Nullable String getLodestoneDimension() {
|
||||||
this.lodestoneTracked = lodestoneTracked;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getLodestoneDimension() {
|
|
||||||
return lodestoneDimension;
|
return lodestoneDimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLodestoneDimension(@Nullable String lodestoneDimension) {
|
public @Nullable Position getLodestonePosition() {
|
||||||
this.lodestoneDimension = lodestoneDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Position getLodestonePosition() {
|
|
||||||
return lodestonePosition;
|
return lodestonePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLodestonePosition(@Nullable Position lodestonePosition) {
|
public static class Builder extends ItemMetaBuilder {
|
||||||
this.lodestonePosition = lodestonePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private boolean lodestoneTracked;
|
||||||
public boolean hasNbt() {
|
private String lodestoneDimension;
|
||||||
return true;
|
private Position lodestonePosition;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Builder lodestoneTracked(boolean lodestoneTracked) {
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
this.lodestoneTracked = lodestoneTracked;
|
||||||
if (!(itemMeta instanceof CompassMeta))
|
this.nbt.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
|
||||||
return false;
|
return this;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lodestonePosition != null) {
|
public Builder lodestoneDimension(@Nullable String lodestoneDimension) {
|
||||||
NBTCompound posCompound = new NBTCompound();
|
this.lodestoneDimension = lodestoneDimension;
|
||||||
posCompound.setInt("X", (int) lodestonePosition.getX());
|
|
||||||
posCompound.setInt("Y", (int) lodestonePosition.getY());
|
if (lodestoneDimension != null) {
|
||||||
posCompound.setInt("Z", (int) lodestonePosition.getZ());
|
this.nbt.setString("LodestoneDimension", lodestoneDimension);
|
||||||
compound.set("LodestonePos", posCompound);
|
} else {
|
||||||
|
this.nbt.removeTag("LodestoneDimension");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
public Builder lodestonePosition(@Nullable Position lodestonePosition) {
|
||||||
@Override
|
this.lodestonePosition = lodestonePosition;
|
||||||
public ItemMeta clone() {
|
|
||||||
CompassMeta compassMeta = (CompassMeta) super.clone();
|
|
||||||
compassMeta.lodestoneTracked = lodestoneTracked;
|
|
||||||
compassMeta.lodestoneDimension = lodestoneDimension;
|
|
||||||
compassMeta.lodestonePosition = CloneUtils.optionalClone(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;
|
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.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
import net.minestom.server.utils.NBTUtils;
|
import net.minestom.server.utils.NBTUtils;
|
||||||
import net.minestom.server.utils.clone.CloneUtils;
|
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
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;
|
public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||||
private ItemStack projectile1, projectile2, projectile3;
|
|
||||||
|
|
||||||
private boolean charged;
|
private final boolean triple;
|
||||||
|
private final ItemStack projectile1, projectile2, projectile3;
|
||||||
/**
|
private final 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");
|
|
||||||
|
|
||||||
|
protected CrossbowMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||||
|
boolean triple,
|
||||||
|
ItemStack projectile1, ItemStack projectile2, ItemStack projectile3,
|
||||||
|
boolean charged) {
|
||||||
|
super(metaBuilder);
|
||||||
|
this.triple = triple;
|
||||||
this.projectile1 = projectile1;
|
this.projectile1 = projectile1;
|
||||||
this.projectile2 = projectile2;
|
this.projectile2 = projectile2;
|
||||||
this.projectile3 = projectile3;
|
this.projectile3 = projectile3;
|
||||||
this.triple = true;
|
this.charged = charged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,27 +46,27 @@ public class CrossbowMeta extends ItemMeta {
|
|||||||
/**
|
/**
|
||||||
* Gets the first projectile.
|
* 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;
|
return projectile1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the second projectile.
|
* 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;
|
return projectile2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the third projectile.
|
* 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;
|
return projectile3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,115 +79,112 @@ public class CrossbowMeta extends ItemMeta {
|
|||||||
return charged;
|
return charged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Makes the bow charged or uncharged.
|
|
||||||
*
|
|
||||||
* @param charged true to make the crossbow charged, false otherwise
|
|
||||||
*/
|
|
||||||
public void setCharged(boolean charged) {
|
|
||||||
this.charged = charged;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private boolean triple;
|
||||||
public boolean hasNbt() {
|
private ItemStack projectile1, projectile2, projectile3 = ItemStack.AIR;
|
||||||
return projectile1 != null && !projectile1.isAir();
|
private boolean charged;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
* Sets the projectile of this crossbow.
|
||||||
if (!(itemMeta instanceof CrossbowMeta))
|
*
|
||||||
return false;
|
* @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;
|
NBTList<NBTCompound> chargedProjectiles = new NBTList<>(NBTTypes.TAG_Compound);
|
||||||
final boolean checkCount = triple && crossbowMeta.triple;
|
if (!projectile.isAir()) {
|
||||||
if (!checkCount)
|
chargedProjectiles.add(getItemCompound(projectile));
|
||||||
return false;
|
}
|
||||||
|
this.nbt.set("ChargedProjectiles", chargedProjectiles);
|
||||||
|
|
||||||
if (projectile1.isSimilar(crossbowMeta.projectile1) &&
|
return this;
|
||||||
projectile2.isSimilar(crossbowMeta.projectile2) &&
|
|
||||||
projectile3.isSimilar(crossbowMeta.projectile3)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
this.projectile1 = projectile1;
|
||||||
public void read(@NotNull NBTCompound compound) {
|
this.projectile2 = projectile2;
|
||||||
if (compound.containsKey("ChargedProjectiles")) {
|
this.projectile3 = projectile3;
|
||||||
final NBTList<NBTCompound> projectilesList = compound.getList("ChargedProjectiles");
|
this.triple = true;
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
return this;
|
||||||
NBTUtils.loadDataIntoItem(itemStack, tagsCompound);
|
}
|
||||||
|
|
||||||
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) {
|
@Override
|
||||||
projectile1 = itemStack;
|
public @NotNull CrossbowMeta build() {
|
||||||
} else if (index == 2) {
|
return new CrossbowMeta(this, triple, projectile1, projectile2, projectile3, charged);
|
||||||
projectile2 = itemStack;
|
}
|
||||||
} else if (index == 3) {
|
|
||||||
projectile3 = itemStack;
|
@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")) {
|
if (nbtCompound.containsKey("Charged")) {
|
||||||
this.charged = compound.getByte("Charged") == 1;
|
charged(nbtCompound.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 (projectile2 != null) {
|
|
||||||
chargedProjectiles.add(getItemCompound(projectile2));
|
|
||||||
}
|
|
||||||
if (projectile3 != null) {
|
|
||||||
chargedProjectiles.add(getItemCompound(projectile3));
|
|
||||||
}
|
|
||||||
compound.set("ChargedProjectiles", chargedProjectiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charged) {
|
@Override
|
||||||
compound.setByte("Charged", (byte) (charged ? 1 : 0));
|
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;
|
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.Enchantment;
|
||||||
|
import net.minestom.server.item.ItemMeta;
|
||||||
|
import net.minestom.server.item.ItemMetaBuilder;
|
||||||
import net.minestom.server.utils.NBTUtils;
|
import net.minestom.server.utils.NBTUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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.
|
* Gets the stored enchantment map.
|
||||||
@ -20,76 +27,41 @@ public class EnchantedBookMeta extends ItemMeta {
|
|||||||
*
|
*
|
||||||
* @return an unmodifiable map containing the item stored enchantments
|
* @return an unmodifiable map containing the item stored enchantments
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull Map<Enchantment, Short> getStoredEnchantmentMap() {
|
||||||
public Map<Enchantment, Short> getStoredEnchantmentMap() {
|
|
||||||
return Collections.unmodifiableMap(storedEnchantmentMap);
|
return Collections.unmodifiableMap(storedEnchantmentMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Sets a stored enchantment level.
|
|
||||||
*
|
private Map<Enchantment, Short> enchantments = new HashMap<>();
|
||||||
* @param enchantment the enchantment type
|
|
||||||
* @param level the enchantment level
|
public @NotNull Builder enchantments(Map<Enchantment, Short> enchantments) {
|
||||||
*/
|
this.enchantments = enchantments;
|
||||||
public void setStoredEnchantment(@NotNull Enchantment enchantment, short level) {
|
NBTUtils.writeEnchant(nbt, "StoredEnchantments", enchantments);
|
||||||
if (level < 1) {
|
return this;
|
||||||
removeStoredEnchantment(enchantment);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storedEnchantmentMap.put(enchantment, level);
|
public @NotNull Builder enchantment(Enchantment enchantment, short level) {
|
||||||
}
|
this.enchantments.put(enchantment, level);
|
||||||
|
enchantments(enchantments);
|
||||||
/**
|
return this;
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NBTCompound compound) {
|
public @NotNull EnchantedBookMeta build() {
|
||||||
if (!storedEnchantmentMap.isEmpty()) {
|
return new EnchantedBookMeta(this, enchantments);
|
||||||
NBTUtils.writeEnchant(compound, "StoredEnchantments", storedEnchantmentMap);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@Override
|
||||||
@Override
|
public void read(@NotNull NBTCompound nbtCompound) {
|
||||||
public ItemMeta clone() {
|
if (nbtCompound.containsKey("StoredEnchantments")) {
|
||||||
EnchantedBookMeta enchantedBookMeta = (EnchantedBookMeta) super.clone();
|
NBTUtils.loadEnchantments(nbtCompound.getList("StoredEnchantments"), this::enchantment);
|
||||||
enchantedBookMeta.storedEnchantmentMap.putAll(storedEnchantmentMap);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return enchantedBookMeta;
|
@Override
|
||||||
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
|
return Builder::new;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,92 +1,52 @@
|
|||||||
package net.minestom.server.item.metadata;
|
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 net.minestom.server.item.firework.FireworkEffect;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
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() {
|
public FireworkEffect getFireworkEffect() {
|
||||||
return fireworkEffect;
|
return fireworkEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private FireworkEffect fireworkEffect;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public Builder effect(@Nullable FireworkEffect fireworkEffect) {
|
||||||
* {@inheritDoc}
|
this.fireworkEffect = fireworkEffect;
|
||||||
*/
|
this.nbt.set("Explosion", this.fireworkEffect.asCompound());
|
||||||
@Override
|
return this;
|
||||||
public boolean hasNbt() {
|
|
||||||
return this.hasFireworkEffect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
|
||||||
if (!(itemMeta instanceof FireworkEffectMeta)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) itemMeta;
|
@Override
|
||||||
return fireworkEffectMeta.fireworkEffect == this.fireworkEffect;
|
public @NotNull FireworkEffectMeta build() {
|
||||||
}
|
return new FireworkEffectMeta(this, fireworkEffect);
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void read(@NotNull NBTCompound compound) {
|
|
||||||
if (compound.containsKey("Explosion")) {
|
|
||||||
this.fireworkEffect = FireworkEffect.fromCompound(compound.getCompound("Explosion"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public void read(@NotNull NBTCompound nbtCompound) {
|
||||||
|
if (nbtCompound.containsKey("Explosion")) {
|
||||||
|
effect(FireworkEffect.fromCompound(nbtCompound.getCompound("Explosion")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* {@inheritDoc}
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
*/
|
return Builder::new;
|
||||||
@Override
|
|
||||||
public void write(@NotNull NBTCompound compound) {
|
|
||||||
if (this.fireworkEffect != null) {
|
|
||||||
compound.set("Explosion", this.fireworkEffect.asCompound());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* {@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;
|
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 net.minestom.server.item.firework.FireworkEffect;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
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.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
public class FireworkMeta extends ItemMeta implements ItemMetaBuilder.Provider<FireworkMeta.Builder> {
|
||||||
* Represents a firework rocket meta data and its effects.
|
|
||||||
*/
|
|
||||||
public class FireworkMeta extends ItemMeta {
|
|
||||||
|
|
||||||
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
|
private final List<FireworkEffect> effects;
|
||||||
private byte flightDuration;
|
private final byte flightDuration;
|
||||||
|
|
||||||
/**
|
protected FireworkMeta(@NotNull ItemMetaBuilder metaBuilder, List<FireworkEffect> effects,
|
||||||
* Adds a firework effect to this firework.
|
byte flightDuration) {
|
||||||
*
|
super(metaBuilder);
|
||||||
* @param effect The firework effect to be added.
|
this.effects = new ArrayList<>(effects);
|
||||||
*/
|
|
||||||
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) {
|
|
||||||
this.flightDuration = flightDuration;
|
this.flightDuration = flightDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List<FireworkEffect> getEffects() {
|
||||||
* Returns the flight duration of this firework
|
return Collections.unmodifiableList(effects);
|
||||||
* @return the flight duration of this firework
|
}
|
||||||
*/
|
|
||||||
public byte getFlightDuration() {
|
public byte getFlightDuration() {
|
||||||
return flightDuration;
|
return flightDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean hasNbt() {
|
|
||||||
return this.flightDuration == 0 || !this.effects.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
|
||||||
* {@inheritDoc}
|
private byte flightDuration;
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public Builder effects(List<FireworkEffect> effects) {
|
||||||
* {@inheritDoc}
|
this.effects = effects;
|
||||||
*/
|
handleCompound("Fireworks", nbtCompound -> {
|
||||||
@Override
|
NBTList<NBTCompound> explosions = new NBTList<>(NBTTypes.TAG_Compound);
|
||||||
public void read(@NotNull NBTCompound compound) {
|
for (FireworkEffect effect : this.effects) {
|
||||||
this.effects.clear();
|
explosions.add(effect.asCompound());
|
||||||
if (compound.containsKey("Fireworks")) {
|
}
|
||||||
NBTCompound fireworksCompound = compound.getCompound("Fireworks");
|
nbtCompound.set("Explosions", explosions);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
if (fireworksCompound.containsKey("Flight")) {
|
public Builder flightDuration(byte flightDuration) {
|
||||||
this.flightDuration = fireworksCompound.getAsByte("Flight");
|
this.flightDuration = flightDuration;
|
||||||
}
|
handleCompound("Fireworks", nbtCompound -> {
|
||||||
|
nbtCompound.setByte("Flight", this.flightDuration);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
if (fireworksCompound.containsKey("Explosions")) {
|
@Override
|
||||||
NBTList<NBTCompound> explosions = fireworksCompound.getList("Explosions");
|
public @NotNull FireworkMeta build() {
|
||||||
|
return new FireworkMeta(this, effects, flightDuration);
|
||||||
|
}
|
||||||
|
|
||||||
for (NBTCompound explosion : explosions) {
|
@Override
|
||||||
this.effects.add(FireworkEffect.fromCompound(explosion));
|
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);
|
@Override
|
||||||
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
compound.set("Fireworks", fireworksCompound);
|
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;
|
package net.minestom.server.item.metadata;
|
||||||
|
|
||||||
import net.minestom.server.chat.ChatColor;
|
|
||||||
import net.minestom.server.color.Color;
|
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.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
|
|
||||||
/**
|
import java.util.function.Supplier;
|
||||||
* Represents the item meta for leather armor parts.
|
|
||||||
*/
|
|
||||||
public class LeatherArmorMeta extends ItemMeta {
|
|
||||||
private boolean modified;
|
|
||||||
private Color color;
|
|
||||||
|
|
||||||
/**
|
public class LeatherArmorMeta extends ItemMeta implements ItemMetaBuilder.Provider<LeatherArmorMeta.Builder> {
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private final Color color;
|
||||||
* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
protected LeatherArmorMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable Color color) {
|
||||||
* Sets the color of this leather armor piece.
|
super(metaBuilder);
|
||||||
*
|
|
||||||
* @param color the new color
|
|
||||||
*/
|
|
||||||
public void setColor(@NotNull Color color) {
|
|
||||||
this.modified = !color.equals(this.color);
|
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @Nullable Color getColor() {
|
||||||
* Gets the color of this leather armor piece.
|
return color;
|
||||||
*
|
|
||||||
* @return the color
|
|
||||||
*/
|
|
||||||
public @NotNull Color getColor() {
|
|
||||||
return this.color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Resets the color to the default leather one.
|
|
||||||
*/
|
|
||||||
public void reset() {
|
|
||||||
this.color = new Color(0, 0, 0);
|
|
||||||
this.modified = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private Color color;
|
||||||
* Gets the red component.
|
|
||||||
*
|
|
||||||
* @return the red component
|
|
||||||
* @deprecated Use {@link #getColor}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public int getRed() {
|
|
||||||
return this.color.getRed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public Builder color(@Nullable Color color) {
|
||||||
* Gets the green component.
|
this.color = color;
|
||||||
*
|
handleCompound("display", nbtCompound -> {
|
||||||
* @return the green component
|
if (color != null) {
|
||||||
* @deprecated Use {@link #getColor}
|
nbtCompound.setInt("color", color.asRGB());
|
||||||
*/
|
} else {
|
||||||
@Deprecated
|
nbtCompound.removeTag("color");
|
||||||
public int getGreen() {
|
}
|
||||||
return this.color.getGreen();
|
});
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets the blue component.
|
public @NotNull LeatherArmorMeta build() {
|
||||||
*
|
return new LeatherArmorMeta(this, color);
|
||||||
* @return the blue component
|
}
|
||||||
* @deprecated Use {@link #getColor}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public int getBlue() {
|
|
||||||
return this.color.getBlue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets if the color of this armor piece have been changed.
|
public void read(@NotNull NBTCompound nbtCompound) {
|
||||||
*
|
if (nbtCompound.containsKey("display")) {
|
||||||
* @return true if the color has been changed, false otherwise
|
final NBTCompound displayCompound = nbtCompound.getCompound("display");
|
||||||
*/
|
if (displayCompound.containsKey("color")) {
|
||||||
public boolean isModified() {
|
color(new Color(displayCompound.getInt("color")));
|
||||||
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
|
||||||
* {@inheritDoc}
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
*/
|
return Builder::new;
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@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.MinecraftServer;
|
||||||
import net.minestom.server.chat.ChatColor;
|
import net.minestom.server.chat.ChatColor;
|
||||||
import net.minestom.server.color.Color;
|
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 net.minestom.server.utils.clone.PublicCloneable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class MapMeta extends ItemMeta {
|
public class MapMeta extends ItemMeta {
|
||||||
|
|
||||||
private int mapId;
|
private final int mapId;
|
||||||
private int mapScaleDirection = 1;
|
private final int mapScaleDirection;
|
||||||
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
|
private final List<MapDecoration> decorations;
|
||||||
private Color mapColor = new Color(0, 0, 0);
|
private final Color mapColor;
|
||||||
|
|
||||||
public MapMeta() {
|
protected MapMeta(ItemMetaBuilder metaBuilder,
|
||||||
}
|
int mapId,
|
||||||
|
int mapScaleDirection,
|
||||||
public MapMeta(int id) {
|
@NotNull List<MapDecoration> decorations,
|
||||||
this.mapId = id;
|
@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;
|
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.
|
* Gets the map scale direction.
|
||||||
*
|
*
|
||||||
@ -54,15 +53,6 @@ public class MapMeta extends ItemMeta {
|
|||||||
return mapScaleDirection;
|
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.
|
* Gets the map decorations.
|
||||||
*
|
*
|
||||||
@ -72,15 +62,6 @@ public class MapMeta extends ItemMeta {
|
|||||||
return decorations;
|
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.
|
* Gets the map color.
|
||||||
*
|
*
|
||||||
@ -101,94 +82,28 @@ public class MapMeta extends ItemMeta {
|
|||||||
return this.mapColor;
|
return this.mapColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private int mapId;
|
||||||
* Changes the map color.
|
private int mapScaleDirection = 1;
|
||||||
*
|
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
|
||||||
* @param color the new map color
|
private Color mapColor = new Color(0, 0, 0);
|
||||||
*/
|
|
||||||
public void setMapColor(@NotNull Color color) {
|
|
||||||
this.mapColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Builder mapId(int value) {
|
||||||
public boolean hasNbt() {
|
this.mapId = value;
|
||||||
return true;
|
this.nbt.setInt("map", mapId);
|
||||||
}
|
return this;
|
||||||
|
|
||||||
@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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.containsKey("map_scale_direction")) {
|
public Builder mapScaleDirection(int value) {
|
||||||
this.mapScaleDirection = compound.getAsInt("map_scale_direction");
|
this.mapScaleDirection = value;
|
||||||
|
this.nbt.setInt("map_scale_direction", value);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.containsKey("Decorations")) {
|
public Builder decorations(List<MapDecoration> value) {
|
||||||
final NBTList<NBTCompound> decorationsList = compound.getList("Decorations");
|
this.decorations = value;
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
NBTList<NBTCompound> decorationsList = new NBTList<>(NBTTypes.TAG_Compound);
|
||||||
for (MapDecoration decoration : decorations) {
|
for (MapDecoration decoration : decorations) {
|
||||||
NBTCompound decorationCompound = new NBTCompound();
|
NBTCompound decorationCompound = new NBTCompound();
|
||||||
@ -200,29 +115,80 @@ public class MapMeta extends ItemMeta {
|
|||||||
|
|
||||||
decorationsList.add(decorationCompound);
|
decorationsList.add(decorationCompound);
|
||||||
}
|
}
|
||||||
compound.set("Decorations", decorationsList);
|
this.nbt.set("Decorations", decorationsList);
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
public Builder mapColor(Color value) {
|
||||||
|
this.mapColor = value;
|
||||||
|
|
||||||
NBTCompound displayCompound;
|
NBTCompound displayCompound;
|
||||||
if (compound.containsKey("display")) {
|
if (nbt.containsKey("display")) {
|
||||||
displayCompound = compound.getCompound("display");
|
displayCompound = nbt.getCompound("display");
|
||||||
} else {
|
} else {
|
||||||
displayCompound = new NBTCompound();
|
displayCompound = new NBTCompound();
|
||||||
|
this.nbt.set("display", displayCompound);
|
||||||
}
|
}
|
||||||
displayCompound.setInt("MapColor", mapColor.asRGB());
|
displayCompound.setInt("MapColor", mapColor.asRGB());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
return this;
|
||||||
@Override
|
}
|
||||||
public ItemMeta clone() {
|
|
||||||
MapMeta mapMeta = (MapMeta) super.clone();
|
@Override
|
||||||
mapMeta.setMapId(mapId);
|
public @NotNull ItemMeta build() {
|
||||||
mapMeta.setMapScaleDirection(mapScaleDirection);
|
return new MapMeta(this, mapId, mapScaleDirection, decorations, mapColor);
|
||||||
mapMeta.decorations = CloneUtils.cloneCopyOnWriteArrayList(decorations);
|
}
|
||||||
mapMeta.setMapColor(mapColor);
|
|
||||||
return mapMeta;
|
@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> {
|
public static class MapDecoration implements PublicCloneable<MapDecoration> {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package net.minestom.server.item.metadata;
|
package net.minestom.server.item.metadata;
|
||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
|
||||||
import net.minestom.server.entity.PlayerSkin;
|
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 net.minestom.server.utils.Utils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -10,152 +11,85 @@ import org.jglrxavpok.hephaistos.nbt.NBTList;
|
|||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
public class PlayerHeadMeta extends ItemMeta implements ItemMetaBuilder.Provider<PlayerHeadMeta.Builder> {
|
||||||
* Represents a skull that can have an owner.
|
|
||||||
*/
|
|
||||||
public class PlayerHeadMeta extends ItemMeta {
|
|
||||||
|
|
||||||
private UUID skullOwner;
|
private final UUID skullOwner;
|
||||||
private PlayerSkin playerSkin;
|
private final PlayerSkin playerSkin;
|
||||||
|
|
||||||
/**
|
protected PlayerHeadMeta(@NotNull ItemMetaBuilder metaBuilder, UUID skullOwner,
|
||||||
* Sets the owner of the skull.
|
PlayerSkin playerSkin) {
|
||||||
*
|
super(metaBuilder);
|
||||||
* @param player The new owner of the skull.
|
this.skullOwner = skullOwner;
|
||||||
* @return {@code true} if the owner was successfully set, otherwise {@code false}.
|
this.playerSkin = playerSkin;
|
||||||
*/
|
|
||||||
public boolean setOwningPlayer(@NotNull Player player) {
|
|
||||||
if (player.getSkin() != null) {
|
|
||||||
this.skullOwner = player.getUuid();
|
|
||||||
this.playerSkin = player.getSkin();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the owner of the head.
|
|
||||||
*
|
|
||||||
* @return The head's owner.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public UUID getSkullOwner() {
|
public UUID getSkullOwner() {
|
||||||
return skullOwner;
|
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() {
|
public PlayerSkin getPlayerSkin() {
|
||||||
return playerSkin;
|
return playerSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Changes the skin of the head.
|
|
||||||
*
|
|
||||||
* @param playerSkin The new skin for the head.
|
|
||||||
*/
|
|
||||||
public void setPlayerSkin(@NotNull PlayerSkin playerSkin) {
|
|
||||||
this.playerSkin = playerSkin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private UUID skullOwner;
|
||||||
* {@inheritDoc}
|
private PlayerSkin playerSkin;
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean hasNbt() {
|
|
||||||
return this.skullOwner != null || playerSkin != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public Builder skullOwner(@Nullable UUID skullOwner) {
|
||||||
* {@inheritDoc}
|
this.skullOwner = skullOwner;
|
||||||
*/
|
handleCompound("SkullOwner", nbtCompound -> {
|
||||||
@Override
|
nbtCompound.setIntArray("Id", Utils.uuidToIntArray(this.skullOwner));
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
});
|
||||||
if (!(itemMeta instanceof PlayerHeadMeta))
|
return this;
|
||||||
return false;
|
}
|
||||||
final PlayerHeadMeta playerHeadMeta = (PlayerHeadMeta) itemMeta;
|
|
||||||
return playerHeadMeta.playerSkin == playerSkin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public Builder playerSkin(@Nullable PlayerSkin playerSkin) {
|
||||||
* {@inheritDoc}
|
this.playerSkin = playerSkin;
|
||||||
*/
|
handleCompound("SkullOwner", nbtCompound -> {
|
||||||
@Override
|
NBTList<NBTCompound> textures = new NBTList<>(NBTTypes.TAG_Compound);
|
||||||
public void read(@NotNull NBTCompound compound) {
|
String value = this.playerSkin.getTextures() == null ? "" : this.playerSkin.getTextures();
|
||||||
if (compound.containsKey("SkullOwner")) {
|
String signature = this.playerSkin.getSignature() == null ? "" : this.playerSkin.getSignature();
|
||||||
NBTCompound skullOwnerCompound = compound.getCompound("SkullOwner");
|
textures.add(new NBTCompound().setString("Value", value).setString("Signature", signature));
|
||||||
|
nbtCompound.set("Properties", new NBTCompound().set("textures", textures));
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
if (skullOwnerCompound.containsKey("Id")) {
|
@Override
|
||||||
this.skullOwner = Utils.intArrayToUuid(skullOwnerCompound.getIntArray("Id"));
|
public @NotNull PlayerHeadMeta build() {
|
||||||
}
|
return new PlayerHeadMeta(this, skullOwner, playerSkin);
|
||||||
|
}
|
||||||
|
|
||||||
if (skullOwnerCompound.containsKey("Properties")) {
|
@Override
|
||||||
NBTCompound propertyCompound = skullOwnerCompound.getCompound("Properties");
|
public void read(@NotNull NBTCompound nbtCompound) {
|
||||||
|
if (nbtCompound.containsKey("SkullOwner")) {
|
||||||
|
NBTCompound skullOwnerCompound = nbtCompound.getCompound("SkullOwner");
|
||||||
|
|
||||||
if (propertyCompound.containsKey("textures")) {
|
if (skullOwnerCompound.containsKey("Id")) {
|
||||||
NBTList<NBTCompound> textures = propertyCompound.getList("textures");
|
skullOwner(Utils.intArrayToUuid(skullOwnerCompound.getIntArray("Id")));
|
||||||
if (textures != null) {
|
|
||||||
NBTCompound nbt = textures.get(0);
|
|
||||||
this.playerSkin = new PlayerSkin(nbt.getString("Value"), nbt.getString("Signature"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
@Override
|
||||||
NBTList<NBTCompound> textures = new NBTList<>(NBTTypes.TAG_Compound);
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
String value = this.playerSkin.getTextures() == null ? "" : this.playerSkin.getTextures();
|
return Builder::new;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
package net.minestom.server.item.metadata;
|
||||||
|
|
||||||
import net.minestom.server.chat.ChatColor;
|
|
||||||
import net.minestom.server.color.Color;
|
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.CustomPotionEffect;
|
||||||
import net.minestom.server.potion.PotionType;
|
import net.minestom.server.potion.PotionType;
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
import net.minestom.server.utils.clone.CloneUtils;
|
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.NBTList;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<PotionMeta.Builder> {
|
||||||
* 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 {
|
|
||||||
|
|
||||||
private PotionType potionType;
|
private final PotionType potionType;
|
||||||
|
private final List<CustomPotionEffect> customPotionEffects;
|
||||||
|
private final Color color;
|
||||||
|
|
||||||
// Not final because of #clone()
|
protected PotionMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable PotionType potionType,
|
||||||
private List<CustomPotionEffect> customPotionEffects = new CopyOnWriteArrayList<>();
|
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() {
|
public PotionType getPotionType() {
|
||||||
return potionType;
|
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() {
|
public List<CustomPotionEffect> getCustomPotionEffects() {
|
||||||
return customPotionEffects;
|
return customPotionEffects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Color getColor() {
|
||||||
* Changes the color of the potion.
|
return color;
|
||||||
*
|
|
||||||
* @param color the new color of the potion
|
|
||||||
* @deprecated Use {@link #setColor(Color)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setColor(ChatColor color) {
|
|
||||||
this.setColor(color.asColor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Changes the color of the potion.
|
|
||||||
*
|
|
||||||
* @param color the new color of the potion
|
|
||||||
*/
|
|
||||||
public void setColor(@Nullable Color color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private PotionType potionType;
|
||||||
public boolean hasNbt() {
|
private List<CustomPotionEffect> customPotionEffects = new ArrayList<>();
|
||||||
return potionType != null ||
|
private Color color;
|
||||||
!customPotionEffects.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Builder potionType(@NotNull PotionType potionType) {
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
this.potionType = potionType;
|
||||||
if (!(itemMeta instanceof PotionMeta))
|
this.nbt.setString("Potion", potionType.getNamespaceID());
|
||||||
return false;
|
return this;
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.containsKey("CustomPotionEffects")) {
|
public Builder effects(@NotNull List<CustomPotionEffect> customPotionEffects) {
|
||||||
NBTList<NBTCompound> customEffectList = compound.getList("CustomPotionEffects");
|
this.customPotionEffects = 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
NBTList<NBTCompound> potionList = new NBTList<>(NBTTypes.TAG_Compound);
|
||||||
|
|
||||||
for (CustomPotionEffect customPotionEffect : customPotionEffects) {
|
for (CustomPotionEffect customPotionEffect : customPotionEffects) {
|
||||||
NBTCompound potionCompound = new NBTCompound();
|
NBTCompound potionCompound = new NBTCompound();
|
||||||
potionCompound.setByte("Id", customPotionEffect.getId());
|
potionCompound.setByte("Id", customPotionEffect.getId());
|
||||||
@ -142,25 +71,52 @@ public class PotionMeta extends ItemMeta {
|
|||||||
|
|
||||||
potionList.add(potionCompound);
|
potionList.add(potionCompound);
|
||||||
}
|
}
|
||||||
|
this.nbt.set("CustomPotionEffects", potionList);
|
||||||
|
|
||||||
compound.set("CustomPotionEffects", potionList);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color != null) {
|
public Builder color(@NotNull Color color) {
|
||||||
compound.setInt("CustomPotionColor", color.asRGB());
|
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;
|
package net.minestom.server.item.metadata;
|
||||||
|
|
||||||
import net.minestom.server.entity.EntityType;
|
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.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
|
|
||||||
// TODO for which item
|
import java.util.function.Supplier;
|
||||||
public class SpawnEggMeta extends ItemMeta {
|
|
||||||
|
|
||||||
private EntityType entityType;
|
public class SpawnEggMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||||
|
|
||||||
@Override
|
private final EntityType entityType;
|
||||||
public boolean hasNbt() {
|
|
||||||
return entityType != null;
|
protected SpawnEggMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable EntityType entityType) {
|
||||||
|
super(metaBuilder);
|
||||||
|
this.entityType = entityType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public @Nullable EntityType getEntityType() {
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
return entityType;
|
||||||
if (!(itemMeta instanceof SpawnEggMeta))
|
|
||||||
return false;
|
|
||||||
final SpawnEggMeta spawnEggMeta = (SpawnEggMeta) itemMeta;
|
|
||||||
return spawnEggMeta.entityType == entityType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static class Builder extends ItemMetaBuilder {
|
||||||
public void read(@NotNull NBTCompound compound) {
|
|
||||||
if (compound.containsKey("EntityTag")) {
|
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
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NBTCompound compound) {
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
if (!hasNbt())
|
return Builder::new;
|
||||||
return;
|
|
||||||
NBTCompound entityCompound = new NBTCompound();
|
|
||||||
if (entityType != null) {
|
|
||||||
entityCompound.setString("id", entityType.getNamespaceID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@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.Component;
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.minestom.server.adventure.AdventureSerializer;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
@ -11,110 +13,97 @@ import org.jglrxavpok.hephaistos.nbt.NBTString;
|
|||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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 final String author;
|
||||||
private String author;
|
private final String title;
|
||||||
private List<Component> pages = new ArrayList<>();
|
private final List<Component> pages;
|
||||||
|
|
||||||
@Nullable
|
protected WritableBookMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||||
public String getTitle() {
|
@Nullable String author, @Nullable String title,
|
||||||
return title;
|
@NotNull List<@NotNull Component> pages) {
|
||||||
}
|
super(metaBuilder);
|
||||||
|
this.author = author;
|
||||||
public void setTitle(@Nullable String title) {
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.pages = new ArrayList<>(pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable String getAuthor() {
|
||||||
public String getAuthor() {
|
|
||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthor(@Nullable String author) {
|
public @Nullable String getTitle() {
|
||||||
this.author = author;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @NotNull List<@NotNull Component> getPages() {
|
||||||
* Gets an array list containing the book pages.
|
return Collections.unmodifiableList(pages);
|
||||||
* <p>
|
|
||||||
* The list is modifiable.
|
|
||||||
*
|
|
||||||
* @return a modifiable {@link ArrayList} containing the book pages
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public List<Component> getPages() {
|
|
||||||
return pages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class Builder extends ItemMetaBuilder {
|
||||||
* Sets the pages list of this book.
|
|
||||||
*
|
|
||||||
* @param pages the pages list
|
|
||||||
*/
|
|
||||||
public void setPages(@NotNull List<Component> pages) {
|
|
||||||
this.pages = pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private String author;
|
||||||
public boolean hasNbt() {
|
private String title;
|
||||||
return !pages.isEmpty();
|
private List<Component> pages = new ArrayList<>();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Builder author(@Nullable String author) {
|
||||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
this.author = author;
|
||||||
if (!(itemMeta instanceof WritableBookMeta))
|
handleNullable(author, "author", nbt,
|
||||||
return false;
|
() -> new NBTString(Objects.requireNonNull(author)));
|
||||||
final WritableBookMeta writableBookMeta = (WritableBookMeta) itemMeta;
|
return this;
|
||||||
return writableBookMeta.pages.equals(pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(@NotNull NBTCompound compound) {
|
|
||||||
|
|
||||||
if (compound.containsKey("title")) {
|
|
||||||
this.title = compound.getString("title");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.containsKey("author")) {
|
public Builder title(@Nullable String title) {
|
||||||
this.author = compound.getString("author");
|
this.title = title;
|
||||||
|
handleNullable(title, "title", nbt,
|
||||||
|
() -> new NBTString(Objects.requireNonNull(title)));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.containsKey("pages")) {
|
public Builder pages(@NotNull List<@NotNull Component> pages) {
|
||||||
final NBTList<NBTString> list = compound.getList("pages");
|
this.pages = pages;
|
||||||
for (NBTString page : list) {
|
|
||||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
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
|
@Override
|
||||||
public void write(@NotNull NBTCompound compound) {
|
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||||
|
return WritableBookMeta.Builder::new;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@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.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.minestom.server.adventure.AdventureSerializer;
|
import net.minestom.server.adventure.AdventureSerializer;
|
||||||
import net.minestom.server.adventure.Localizable;
|
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.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
import org.jglrxavpok.hephaistos.nbt.*;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class WrittenBookMeta extends ItemMeta {
|
public class WrittenBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<WrittenBookMeta.Builder> {
|
||||||
|
|
||||||
private boolean resolved;
|
private final boolean resolved;
|
||||||
private WrittenBookGeneration generation;
|
private final WrittenBookGeneration generation;
|
||||||
private String author;
|
private final String author;
|
||||||
private String title;
|
private final String title;
|
||||||
private List<Component> pages = new ArrayList<>();
|
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() {
|
public boolean isResolved() {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @Nullable WrittenBookGeneration getGeneration() {
|
||||||
* 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() {
|
|
||||||
return generation;
|
return generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @Nullable String getAuthor() {
|
||||||
* 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() {
|
|
||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @Nullable String getTitle() {
|
||||||
* 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() {
|
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public @NotNull List<@NotNull Component> getPages() {
|
||||||
* Sets the title of the book.
|
return Collections.unmodifiableList(pages);
|
||||||
*
|
|
||||||
* @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 enum WrittenBookGeneration {
|
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
|
* Creates a written book meta from an Adventure book. This meta will not be
|
||||||
* resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}.
|
* 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
|
* @param localizable who the book is for
|
||||||
*
|
|
||||||
* @return the meta
|
* @return the meta
|
||||||
*/
|
*/
|
||||||
public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) {
|
public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) {
|
||||||
// make the book
|
return new Builder()
|
||||||
WrittenBookMeta meta = new WrittenBookMeta();
|
.resolved(false)
|
||||||
meta.resolved = false;
|
.generation(WrittenBookGeneration.ORIGINAL)
|
||||||
meta.generation = WrittenBookGeneration.ORIGINAL;
|
.author(AdventureSerializer.translateAndSerialize(book.author(), localizable))
|
||||||
meta.author = AdventureSerializer.translateAndSerialize(book.author(), localizable);
|
.title(AdventureSerializer.translateAndSerialize(book.title(), localizable))
|
||||||
meta.title = AdventureSerializer.translateAndSerialize(book.title(), localizable);
|
.pages(book.pages())
|
||||||
meta.pages = new ArrayList<>();
|
.build();
|
||||||
meta.pages.addAll(book.pages());
|
}
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public ItemStack apply(@NotNull ItemStack item, int newAmount) {
|
public ItemStack apply(@NotNull ItemStack item, int newAmount) {
|
||||||
if (newAmount <= 0)
|
if (newAmount <= 0)
|
||||||
return ItemStack.getAirItem();
|
return ItemStack.AIR;
|
||||||
|
|
||||||
item.setAmount((byte) newAmount);
|
return item.withAmount(newAmount);
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -10,7 +10,7 @@ public class AnimationListener {
|
|||||||
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
||||||
final Player.Hand hand = packet.hand;
|
final Player.Hand hand = packet.hand;
|
||||||
final ItemStack itemStack = player.getItemInHand(hand);
|
final ItemStack itemStack = player.getItemInHand(hand);
|
||||||
itemStack.onLeftClick(player, hand);
|
//itemStack.onLeftClick(player, hand);
|
||||||
PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand);
|
PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand);
|
||||||
player.callCancellableEvent(PlayerHandAnimationEvent.class, handAnimationEvent, () -> {
|
player.callCancellableEvent(PlayerHandAnimationEvent.class, handAnimationEvent, () -> {
|
||||||
switch (hand) {
|
switch (hand) {
|
||||||
|
@ -52,7 +52,8 @@ public class BlockPlacementListener {
|
|||||||
final ItemStack usedItem = player.getItemInHand(hand);
|
final ItemStack usedItem = player.getItemInHand(hand);
|
||||||
|
|
||||||
// Interact at block
|
// 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 playerBlockInteractEvent = new PlayerBlockInteractEvent(player, blockPosition, hand, blockFace);
|
||||||
playerBlockInteractEvent.setCancelled(cancel);
|
playerBlockInteractEvent.setCancelled(cancel);
|
||||||
playerBlockInteractEvent.setBlockingItemUse(cancel);
|
playerBlockInteractEvent.setBlockingItemUse(cancel);
|
||||||
@ -85,7 +86,8 @@ public class BlockPlacementListener {
|
|||||||
canPlaceBlock = false; //Spectators can't place blocks
|
canPlaceBlock = false; //Spectators can't place blocks
|
||||||
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
||||||
//Check if the block can placed on the block
|
//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
|
// Block consuming
|
||||||
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
||||||
// Consume the block in the player's hand
|
// Consume the block in the player's hand
|
||||||
final ItemStack newUsedItem = usedItem.consume(1);
|
final ItemStack newUsedItem = usedItem.getStackingRule().apply(usedItem, usedItem.getAmount() - 1);
|
||||||
|
playerInventory.setItemInHand(hand, newUsedItem);
|
||||||
if (newUsedItem != null) {
|
|
||||||
playerInventory.setItemInHand(hand, newUsedItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
refreshChunk = true;
|
refreshChunk = true;
|
||||||
@ -196,8 +195,6 @@ public class BlockPlacementListener {
|
|||||||
if (refreshChunk) {
|
if (refreshChunk) {
|
||||||
chunk.sendChunkSectionUpdate(ChunkUtils.getSectionAt(blockPosition.getY()), player);
|
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) {
|
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
||||||
//Check if the item can break the block with the current item
|
//Check if the item can break the block with the current item
|
||||||
ItemStack itemInMainHand = player.getItemInMainHand();
|
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,
|
sendAcknowledgePacket(player, blockPosition, blockStateId,
|
||||||
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
|
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
|
||||||
return;
|
return;
|
||||||
@ -111,7 +112,7 @@ public class PlayerDiggingListener {
|
|||||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
|
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
|
||||||
|
|
||||||
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
|
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
|
||||||
dropItem(player, droppedItemStack, ItemStack.getAirItem());
|
dropItem(player, droppedItemStack, ItemStack.AIR);
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
|
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
|
||||||
|
|
||||||
@ -123,14 +124,11 @@ public class PlayerDiggingListener {
|
|||||||
|
|
||||||
if (handAmount <= dropAmount) {
|
if (handAmount <= dropAmount) {
|
||||||
// Drop the whole item without copy
|
// Drop the whole item without copy
|
||||||
dropItem(player, handItem, ItemStack.getAirItem());
|
dropItem(player, handItem, ItemStack.AIR);
|
||||||
} else {
|
} else {
|
||||||
// Drop a single item, need a copy
|
// 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);
|
handItem = stackingRule.apply(handItem, handAmount - dropAmount);
|
||||||
|
|
||||||
dropItem(player, droppedItemStack2, handItem);
|
dropItem(player, droppedItemStack2, handItem);
|
||||||
|
@ -15,7 +15,7 @@ public class UseItemListener {
|
|||||||
final PlayerInventory inventory = player.getInventory();
|
final PlayerInventory inventory = player.getInventory();
|
||||||
final Player.Hand hand = packet.hand;
|
final Player.Hand hand = packet.hand;
|
||||||
ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand();
|
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);
|
PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack);
|
||||||
player.callEvent(PlayerUseItemEvent.class, useItemEvent);
|
player.callEvent(PlayerUseItemEvent.class, useItemEvent);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
|
|||||||
public byte button;
|
public byte button;
|
||||||
public short actionNumber;
|
public short actionNumber;
|
||||||
public int mode;
|
public int mode;
|
||||||
public ItemStack item = ItemStack.getAirItem();
|
public ItemStack item = ItemStack.AIR;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
|
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class ClientCreativeInventoryActionPacket extends ClientPlayPacket {
|
public class ClientCreativeInventoryActionPacket extends ClientPlayPacket {
|
||||||
|
|
||||||
public short slot;
|
public short slot;
|
||||||
public ItemStack item = ItemStack.getAirItem();
|
public ItemStack item = ItemStack.AIR;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
|
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
public class ClientEditBookPacket extends ClientPlayPacket {
|
public class ClientEditBookPacket extends ClientPlayPacket {
|
||||||
|
|
||||||
public ItemStack book = ItemStack.getAirItem();
|
public ItemStack book = ItemStack.AIR;
|
||||||
public boolean isSigning;
|
public boolean isSigning;
|
||||||
public Player.Hand hand = Player.Hand.MAIN;
|
public Player.Hand hand = Player.Hand.MAIN;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ public class ClientNameItemPacket extends ClientPlayPacket {
|
|||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
if(itemName.length() > Short.MAX_VALUE) {
|
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);
|
writer.writeSizedString(itemName);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
|||||||
public String[] identifiersToRemove = new String[0];
|
public String[] identifiersToRemove = new String[0];
|
||||||
public ProgressMapping[] progressMappings = new ProgressMapping[0];
|
public ProgressMapping[] progressMappings = new ProgressMapping[0];
|
||||||
|
|
||||||
public AdvancementsPacket() {}
|
public AdvancementsPacket() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
@ -149,14 +150,14 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
|||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
boolean hasParent = reader.readBoolean();
|
boolean hasParent = reader.readBoolean();
|
||||||
if(hasParent) {
|
if (hasParent) {
|
||||||
parentIdentifier = reader.readSizedString(Integer.MAX_VALUE);
|
parentIdentifier = reader.readSizedString(Integer.MAX_VALUE);
|
||||||
} else {
|
} else {
|
||||||
parentIdentifier = null;
|
parentIdentifier = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasDisplay = reader.readBoolean();
|
boolean hasDisplay = reader.readBoolean();
|
||||||
if(hasDisplay) {
|
if (hasDisplay) {
|
||||||
displayData = new DisplayData();
|
displayData = new DisplayData();
|
||||||
displayData.read(reader);
|
displayData.read(reader);
|
||||||
} else {
|
} else {
|
||||||
@ -177,7 +178,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
|||||||
public static class DisplayData implements Writeable, Readable {
|
public static class DisplayData implements Writeable, Readable {
|
||||||
public Component title = Component.empty(); // Only text
|
public Component title = Component.empty(); // Only text
|
||||||
public Component description = 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 FrameType frameType = FrameType.TASK;
|
||||||
public int flags;
|
public int flags;
|
||||||
public String backgroundTexture = "";
|
public String backgroundTexture = "";
|
||||||
@ -205,7 +206,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
|||||||
icon = reader.readItemStack();
|
icon = reader.readItemStack();
|
||||||
frameType = FrameType.values()[reader.readVarInt()];
|
frameType = FrameType.values()[reader.readVarInt()];
|
||||||
flags = reader.readInt();
|
flags = reader.readInt();
|
||||||
if((flags & 0x1) != 0) {
|
if ((flags & 0x1) != 0) {
|
||||||
backgroundTexture = reader.readSizedString(Integer.MAX_VALUE);
|
backgroundTexture = reader.readSizedString(Integer.MAX_VALUE);
|
||||||
} else {
|
} else {
|
||||||
backgroundTexture = null;
|
backgroundTexture = null;
|
||||||
@ -302,7 +303,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
|||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
achieved = reader.readBoolean();
|
achieved = reader.readBoolean();
|
||||||
if(achieved) {
|
if (achieved) {
|
||||||
dateOfAchieving = reader.readLong();
|
dateOfAchieving = reader.readLong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ public class DeclareRecipesPacket implements ServerPacket {
|
|||||||
|
|
||||||
public DeclaredRecipe[] recipes = new DeclaredRecipe[0];
|
public DeclaredRecipe[] recipes = new DeclaredRecipe[0];
|
||||||
|
|
||||||
public DeclareRecipesPacket() {}
|
public DeclareRecipesPacket() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
@ -68,7 +69,7 @@ public class DeclareRecipesPacket implements ServerPacket {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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();
|
width = reader.readVarInt();
|
||||||
height = reader.readVarInt();
|
height = reader.readVarInt();
|
||||||
group = reader.readSizedString(Integer.MAX_VALUE);
|
group = reader.readSizedString(Integer.MAX_VALUE);
|
||||||
ingredients = new Ingredient[width*height];
|
ingredients = new Ingredient[width * height];
|
||||||
for (int i = 0; i < width * height; i++) {
|
for (int i = 0; i < width * height; i++) {
|
||||||
ingredients[i] = new Ingredient();
|
ingredients[i] = new Ingredient();
|
||||||
ingredients[i].read(reader);
|
ingredients[i].read(reader);
|
||||||
@ -442,7 +443,7 @@ public class DeclareRecipesPacket implements ServerPacket {
|
|||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
group = reader.readSizedString(Integer.MAX_VALUE);
|
group = reader.readSizedString(Integer.MAX_VALUE);
|
||||||
ingredient = new Ingredient();
|
ingredient = new Ingredient();
|
||||||
ingredient.read( reader);
|
ingredient.read(reader);
|
||||||
result = reader.readItemStack();
|
result = reader.readItemStack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ public class EntityEquipmentPacket implements ServerPacket {
|
|||||||
public Slot[] slots;
|
public Slot[] slots;
|
||||||
public ItemStack[] itemStacks;
|
public ItemStack[] itemStacks;
|
||||||
|
|
||||||
public EntityEquipmentPacket() {}
|
public EntityEquipmentPacket() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
@ -53,7 +54,7 @@ public class EntityEquipmentPacket implements ServerPacket {
|
|||||||
boolean hasRemaining = true;
|
boolean hasRemaining = true;
|
||||||
List<Slot> slots = new LinkedList<>();
|
List<Slot> slots = new LinkedList<>();
|
||||||
List<ItemStack> stacks = new LinkedList<>();
|
List<ItemStack> stacks = new LinkedList<>();
|
||||||
while(hasRemaining) {
|
while (hasRemaining) {
|
||||||
byte slotEnum = reader.readByte();
|
byte slotEnum = reader.readByte();
|
||||||
hasRemaining = (slotEnum & 0x80) == 0x80;
|
hasRemaining = (slotEnum & 0x80) == 0x80;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.minestom.server.network.packet.server.play;
|
package net.minestom.server.network.packet.server.play;
|
||||||
|
|
||||||
import net.minestom.server.chat.ColoredText;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.chat.JsonMessage;
|
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||||
import net.minestom.server.utils.binary.BinaryReader;
|
import net.minestom.server.utils.binary.BinaryReader;
|
||||||
@ -12,26 +11,27 @@ public class OpenWindowPacket implements ServerPacket {
|
|||||||
|
|
||||||
public int windowId;
|
public int windowId;
|
||||||
public int windowType;
|
public int windowType;
|
||||||
public JsonMessage title = ColoredText.of("");
|
public Component title = Component.text("");
|
||||||
|
|
||||||
public OpenWindowPacket() {}
|
public OpenWindowPacket() {
|
||||||
|
}
|
||||||
|
|
||||||
public OpenWindowPacket(String title) {
|
public OpenWindowPacket(Component title) {
|
||||||
this.title = ColoredText.of(title);
|
this.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
writer.writeVarInt(windowId);
|
writer.writeVarInt(windowId);
|
||||||
writer.writeVarInt(windowType);
|
writer.writeVarInt(windowType);
|
||||||
writer.writeJsonMessage(title);
|
writer.writeComponent(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
windowId = reader.readVarInt();
|
windowId = reader.readVarInt();
|
||||||
windowType = reader.readVarInt();
|
windowType = reader.readVarInt();
|
||||||
title = reader.readJsonMessage(Integer.MAX_VALUE);
|
title = reader.readComponent(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,7 +14,7 @@ public class SetSlotPacket implements ServerPacket {
|
|||||||
public ItemStack itemStack;
|
public ItemStack itemStack;
|
||||||
|
|
||||||
public SetSlotPacket() {
|
public SetSlotPacket() {
|
||||||
itemStack = ItemStack.getAirItem();
|
itemStack = ItemStack.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,7 +96,7 @@ public class TradeListPacket implements ServerPacket {
|
|||||||
result = reader.readItemStack();
|
result = reader.readItemStack();
|
||||||
|
|
||||||
boolean hasSecondItem = reader.readBoolean();
|
boolean hasSecondItem = reader.readBoolean();
|
||||||
if(hasSecondItem) {
|
if (hasSecondItem) {
|
||||||
inputItem2 = reader.readItemStack();
|
inputItem2 = reader.readItemStack();
|
||||||
} else {
|
} else {
|
||||||
inputItem2 = null;
|
inputItem2 = null;
|
||||||
|
@ -17,11 +17,11 @@ public abstract class ShapedRecipe extends Recipe {
|
|||||||
private ItemStack result;
|
private ItemStack result;
|
||||||
|
|
||||||
protected ShapedRecipe(@NotNull String recipeId,
|
protected ShapedRecipe(@NotNull String recipeId,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
@NotNull String group,
|
@NotNull String group,
|
||||||
@Nullable List<DeclareRecipesPacket.Ingredient> ingredients,
|
@Nullable List<DeclareRecipesPacket.Ingredient> ingredients,
|
||||||
@NotNull ItemStack result) {
|
@NotNull ItemStack result) {
|
||||||
super(RecipeType.SHAPED, recipeId);
|
super(RecipeType.SHAPED, recipeId);
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
@ -38,7 +38,7 @@ public abstract class ThreadProvider {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Default thread count in the pool (cores * 2)
|
// 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.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.kyori.adventure.util.Codec;
|
import net.kyori.adventure.util.Codec;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.adventure.AdventureSerializer;
|
|
||||||
import net.minestom.server.attribute.Attribute;
|
import net.minestom.server.attribute.Attribute;
|
||||||
import net.minestom.server.attribute.AttributeOperation;
|
import net.minestom.server.attribute.AttributeOperation;
|
||||||
import net.minestom.server.data.Data;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.data.DataType;
|
|
||||||
import net.minestom.server.inventory.Inventory;
|
import net.minestom.server.inventory.Inventory;
|
||||||
import net.minestom.server.item.Enchantment;
|
import net.minestom.server.item.Enchantment;
|
||||||
|
import net.minestom.server.item.ItemMetaBuilder;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.item.attribute.AttributeSlot;
|
import net.minestom.server.item.attribute.AttributeSlot;
|
||||||
import net.minestom.server.item.attribute.ItemAttribute;
|
import net.minestom.server.item.attribute.ItemAttribute;
|
||||||
import net.minestom.server.item.metadata.ItemMeta;
|
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
import net.minestom.server.utils.binary.BinaryReader;
|
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.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -48,6 +44,7 @@ public final class NBTUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns an {@link NBTCompound} into an Adventure {@link BinaryTagHolder}.
|
* Turns an {@link NBTCompound} into an Adventure {@link BinaryTagHolder}.
|
||||||
|
*
|
||||||
* @param tag the tag, if any
|
* @param tag the tag, if any
|
||||||
* @return the binary tag holder, or {@code null} if the tag was null
|
* @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) {
|
public static void loadAllItems(@NotNull NBTList<NBTCompound> items, @NotNull Inventory destination) {
|
||||||
destination.clear();
|
destination.clear();
|
||||||
for (NBTCompound tag : items) {
|
for (NBTCompound tag : items) {
|
||||||
Material item = Registries.getMaterial(tag.getString("id"));
|
Material material = Registries.getMaterial(tag.getString("id"));
|
||||||
if (item == Material.AIR) {
|
if (material == Material.AIR) {
|
||||||
item = Material.STONE;
|
material = Material.STONE;
|
||||||
}
|
}
|
||||||
ItemStack stack = new ItemStack(item, tag.getByte("Count"));
|
byte count = tag.getByte("Count");
|
||||||
|
NBTCompound nbtCompound = null;
|
||||||
if (tag.containsKey("tag")) {
|
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);
|
final ItemStack stack = inventory.getItemStack(i);
|
||||||
NBTCompound nbt = new NBTCompound();
|
NBTCompound nbt = new NBTCompound();
|
||||||
|
|
||||||
NBTCompound tag = new NBTCompound();
|
NBTCompound tag = stack.getMeta().toNBT();
|
||||||
saveDataIntoNBT(stack, tag);
|
|
||||||
|
|
||||||
nbt.set("tag", tag);
|
nbt.set("tag", tag);
|
||||||
nbt.setByte("Slot", (byte) i);
|
nbt.setByte("Slot", (byte) i);
|
||||||
nbt.setByte("Count", stack.getAmount());
|
nbt.setByte("Count", (byte) stack.getAmount());
|
||||||
nbt.setString("id", stack.getMaterial().getName());
|
nbt.setString("id", stack.getMaterial().getName());
|
||||||
|
|
||||||
list.add(nbt);
|
list.add(nbt);
|
||||||
@ -113,48 +111,60 @@ public final class NBTUtils {
|
|||||||
nbt.set(listName, enchantList);
|
nbt.set(listName, enchantList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
public static ItemStack readItemStack(@NotNull BinaryReader reader) {
|
public static ItemStack readItemStack(@NotNull BinaryReader reader) {
|
||||||
final boolean present = reader.readBoolean();
|
final boolean present = reader.readBoolean();
|
||||||
|
|
||||||
if (!present) {
|
if (!present) {
|
||||||
return ItemStack.getAirItem();
|
return ItemStack.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int id = reader.readVarInt();
|
final int id = reader.readVarInt();
|
||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
// Drop mode
|
// Drop mode
|
||||||
return ItemStack.getAirItem();
|
return ItemStack.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Material material = Material.fromId((short) id);
|
final Material material = Material.fromId((short) id);
|
||||||
final byte count = reader.readByte();
|
final byte count = reader.readByte();
|
||||||
ItemStack item = new ItemStack(material, count);
|
NBTCompound nbtCompound = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final NBT itemNBT = reader.readTag();
|
final NBT itemNBT = reader.readTag();
|
||||||
if (itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data
|
if (itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data
|
||||||
NBTCompound nbt = (NBTCompound) itemNBT;
|
nbtCompound = (NBTCompound) itemNBT;
|
||||||
loadDataIntoItem(item, nbt);
|
|
||||||
}
|
}
|
||||||
} catch (IOException | NBTException e) {
|
} catch (IOException | NBTException e) {
|
||||||
MinecraftServer.getExceptionManager().handleException(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")
|
@SuppressWarnings("ConstantConditions")
|
||||||
public static void loadDataIntoItem(@NotNull ItemStack item, @NotNull NBTCompound nbt) {
|
public static void loadDataIntoMeta(@NotNull ItemMetaBuilder metaBuilder, @NotNull NBTCompound nbt) {
|
||||||
if (nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage"));
|
if (nbt.containsKey("Damage")) metaBuilder.damage(nbt.getInt("Damage"));
|
||||||
if (nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getAsByte("Unbreakable") == 1);
|
if (nbt.containsKey("Unbreakable")) metaBuilder.unbreakable(nbt.getAsByte("Unbreakable") == 1);
|
||||||
if (nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags"));
|
if (nbt.containsKey("HideFlags")) metaBuilder.hideFlag(nbt.getInt("HideFlags"));
|
||||||
if (nbt.containsKey("display")) {
|
if (nbt.containsKey("display")) {
|
||||||
final NBTCompound display = nbt.getCompound("display");
|
final NBTCompound display = nbt.getCompound("display");
|
||||||
if (display.containsKey("Name")) {
|
if (display.containsKey("Name")) {
|
||||||
final String rawName = display.getString("Name");
|
final String rawName = display.getString("Name");
|
||||||
final Component displayName = GsonComponentSerializer.gson().deserialize(rawName);
|
final Component displayName = GsonComponentSerializer.gson().deserialize(rawName);
|
||||||
item.setDisplayName(displayName);
|
metaBuilder.displayName(displayName);
|
||||||
}
|
}
|
||||||
if (display.containsKey("Lore")) {
|
if (display.containsKey("Lore")) {
|
||||||
NBTList<NBTString> loreList = display.getList("Lore");
|
NBTList<NBTString> loreList = display.getList("Lore");
|
||||||
@ -162,19 +172,20 @@ public final class NBTUtils {
|
|||||||
for (NBTString s : loreList) {
|
for (NBTString s : loreList) {
|
||||||
lore.add(GsonComponentSerializer.gson().deserialize(s.getValue()));
|
lore.add(GsonComponentSerializer.gson().deserialize(s.getValue()));
|
||||||
}
|
}
|
||||||
item.setLore(lore);
|
metaBuilder.lore(lore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enchantments
|
// Enchantments
|
||||||
if (nbt.containsKey("Enchantments")) {
|
if (nbt.containsKey("Enchantments")) {
|
||||||
loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment);
|
loadEnchantments(nbt.getList("Enchantments"), metaBuilder::enchantment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
if (nbt.containsKey("AttributeModifiers")) {
|
if (nbt.containsKey("AttributeModifiers")) {
|
||||||
NBTList<NBTCompound> attributes = nbt.getList("AttributeModifiers");
|
List<ItemAttribute> attributes = new ArrayList<>();
|
||||||
for (NBTCompound attributeNBT : attributes) {
|
NBTList<NBTCompound> nbtAttributes = nbt.getList("AttributeModifiers");
|
||||||
|
for (NBTCompound attributeNBT : nbtAttributes) {
|
||||||
final UUID uuid;
|
final UUID uuid;
|
||||||
{
|
{
|
||||||
final int[] uuidArray = attributeNBT.getIntArray("UUID");
|
final int[] uuidArray = attributeNBT.getIntArray("UUID");
|
||||||
@ -208,54 +219,43 @@ public final class NBTUtils {
|
|||||||
// Add attribute
|
// Add attribute
|
||||||
final ItemAttribute itemAttribute =
|
final ItemAttribute itemAttribute =
|
||||||
new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot);
|
new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot);
|
||||||
item.addAttribute(itemAttribute);
|
attributes.add(itemAttribute);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide flags
|
|
||||||
{
|
|
||||||
if (nbt.containsKey("HideFlags")) {
|
|
||||||
item.setHideFlag(nbt.getInt("HideFlags"));
|
|
||||||
}
|
}
|
||||||
|
metaBuilder.attributes(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom model data
|
// Custom model data
|
||||||
{
|
{
|
||||||
if (nbt.containsKey("CustomModelData")) {
|
if (nbt.containsKey("CustomModelData")) {
|
||||||
item.setCustomModelData(nbt.getInt("CustomModelData"));
|
metaBuilder.customModelData(nbt.getInt("CustomModelData"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meta specific field
|
// Meta specific fields
|
||||||
final ItemMeta itemMeta = item.getItemMeta();
|
metaBuilder.read(nbt);
|
||||||
if (itemMeta != null) {
|
|
||||||
itemMeta.read(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ownership
|
// CanPlaceOn
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
if (nbt.containsKey("CanPlaceOn")) {
|
if (nbt.containsKey("CanPlaceOn")) {
|
||||||
NBTList<NBTString> canPlaceOn = nbt.getList("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")) {
|
if (nbt.containsKey("CanDestroy")) {
|
||||||
NBTList<NBTString> canPlaceOn = nbt.getList("CanDestroy");
|
NBTList<NBTString> canDestroy = nbt.getList("CanDestroy");
|
||||||
canPlaceOn.forEach(x -> item.getCanDestroy().add(x.getValue()));
|
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
|
@FunctionalInterface
|
||||||
public interface EnchantmentSetter {
|
public interface EnchantmentSetter {
|
||||||
void applyEnchantment(Enchantment name, short level);
|
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.chat.JsonMessage;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
import net.minestom.server.utils.NBTUtils;
|
|
||||||
import net.minestom.server.utils.SerializerUtils;
|
import net.minestom.server.utils.SerializerUtils;
|
||||||
import net.minestom.server.utils.Utils;
|
import net.minestom.server.utils.Utils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -264,7 +263,14 @@ public class BinaryWriter extends OutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeItemStack(@NotNull ItemStack itemStack) {
|
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) {
|
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
|
@Override
|
||||||
public NBTCompound toNbt() {
|
public NBTCompound toNbt() {
|
||||||
//todo test count might be wrong type
|
//todo test count might be wrong type
|
||||||
NBTCompound nbtCompound = item.toNBT();
|
NBTCompound nbtCompound = item.getMeta().toNBT();
|
||||||
nbtCompound.setString("type", type);
|
nbtCompound.setString("type", type);
|
||||||
return nbtCompound;
|
return nbtCompound;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ public class Main {
|
|||||||
commandManager.register(new EchoCommand());
|
commandManager.register(new EchoCommand());
|
||||||
commandManager.register(new SummonCommand());
|
commandManager.register(new SummonCommand());
|
||||||
commandManager.register(new RemoveCommand());
|
commandManager.register(new RemoveCommand());
|
||||||
|
commandManager.register(new GiveCommand());
|
||||||
|
|
||||||
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));
|
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package demo;
|
package demo;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.AtomicDouble;
|
|
||||||
import demo.generator.ChunkGeneratorDemo;
|
import demo.generator.ChunkGeneratorDemo;
|
||||||
import demo.generator.NoiseTestGenerator;
|
import demo.generator.NoiseTestGenerator;
|
||||||
import net.kyori.adventure.text.Component;
|
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.InventoryType;
|
||||||
import net.minestom.server.inventory.PlayerInventory;
|
import net.minestom.server.inventory.PlayerInventory;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
|
import net.minestom.server.item.ItemTag;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
|
import net.minestom.server.item.metadata.CompassMeta;
|
||||||
import net.minestom.server.monitoring.BenchmarkManager;
|
import net.minestom.server.monitoring.BenchmarkManager;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.ping.ResponseDataConsumer;
|
import net.minestom.server.ping.ResponseDataConsumer;
|
||||||
import net.minestom.server.utils.MathUtils;
|
|
||||||
import net.minestom.server.utils.Position;
|
import net.minestom.server.utils.Position;
|
||||||
import net.minestom.server.utils.Vector;
|
import net.minestom.server.utils.Vector;
|
||||||
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
import net.minestom.server.world.DimensionType;
|
import net.minestom.server.world.DimensionType;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
@ -57,24 +58,40 @@ public class PlayerInit {
|
|||||||
instanceContainer.enableAutoChunkLoad(true);
|
instanceContainer.enableAutoChunkLoad(true);
|
||||||
instanceContainer.setChunkGenerator(chunkGeneratorDemo);
|
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) -> {
|
/*inventory.addInventoryCondition((p, slot, clickType, inventoryConditionResult) -> {
|
||||||
p.sendMessage("click type inventory: " + clickType);
|
p.sendMessage("click type inventory: " + clickType);
|
||||||
System.out.println("slot inv: " + slot)0;
|
System.out.println("slot inv: " + slot)0;
|
||||||
inventoryConditionResult.setCancel(slot == 3);
|
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() {
|
public static void init() {
|
||||||
ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
||||||
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
|
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
|
||||||
|
|
||||||
MinecraftServer.getUpdateManager().addTickMonitor(tickMonitor ->
|
|
||||||
LAST_TICK_TIME.set(tickMonitor.getTickTime()));
|
|
||||||
|
|
||||||
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
||||||
|
|
||||||
Collection<Player> players = connectionManager.getOnlinePlayers();
|
Collection<Player> players = connectionManager.getOnlinePlayers();
|
||||||
@ -85,9 +102,7 @@ public class PlayerInit {
|
|||||||
long ramUsage = benchmarkManager.getUsedMemory();
|
long ramUsage = benchmarkManager.getUsedMemory();
|
||||||
ramUsage /= 1e6; // bytes to MB
|
ramUsage /= 1e6; // bytes to MB
|
||||||
|
|
||||||
final Component header = Component.text("RAM USAGE: " + ramUsage + " 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 footer = benchmarkManager.getCpuMonitoringMessage();
|
final Component footer = benchmarkManager.getCpuMonitoringMessage();
|
||||||
Audiences.players().sendPlayerListHeaderAndFooter(header, footer);
|
Audiences.players().sendPlayerListHeaderAndFooter(header, footer);
|
||||||
|
|
||||||
@ -189,6 +204,7 @@ public class PlayerInit {
|
|||||||
|
|
||||||
globalEventHandler.addEventCallback(PlayerLoginEvent.class, event -> {
|
globalEventHandler.addEventCallback(PlayerLoginEvent.class, event -> {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
|
player.sendMessage("test");
|
||||||
|
|
||||||
event.setSpawningInstance(instanceContainer);
|
event.setSpawningInstance(instanceContainer);
|
||||||
int x = Math.abs(ThreadLocalRandom.current().nextInt()) % 1000 - 250;
|
int x = Math.abs(ThreadLocalRandom.current().nextInt()) % 1000 - 250;
|
||||||
@ -210,19 +226,31 @@ public class PlayerInit {
|
|||||||
player.setPermissionLevel(4);
|
player.setPermissionLevel(4);
|
||||||
|
|
||||||
PlayerInventory inventory = player.getInventory();
|
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);
|
inventory.addItemStack(itemStack);
|
||||||
|
|
||||||
{
|
{
|
||||||
ItemStack item = new ItemStack(Material.DIAMOND_CHESTPLATE, (byte) 1);
|
ItemStack item = ItemStack.builder(Material.DIAMOND_CHESTPLATE)
|
||||||
inventory.setChestplate(item);
|
.displayName(Component.text("test"))
|
||||||
item.setDisplayName(ColoredText.of("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 -> {
|
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 {
|
private <T extends Readable & Writeable> Collection<DynamicTest> checkImplementationPresence(Class<T> packetClass) throws IOException {
|
||||||
ClassPath cp = ClassPath.from(ClassLoader.getSystemClassLoader());
|
ClassPath cp = ClassPath.from(ClassLoader.getSystemClassLoader());
|
||||||
List<DynamicTest> allTests = new LinkedList<>();
|
List<DynamicTest> allTests = new LinkedList<>();
|
||||||
for(ClassPath.ClassInfo classInfo : cp.getAllClasses()) {
|
for (ClassPath.ClassInfo classInfo : cp.getAllClasses()) {
|
||||||
if(!classInfo.getPackageName().startsWith("net.minestom.server.network.packet"))
|
if (!classInfo.getPackageName().startsWith("net.minestom.server.network.packet"))
|
||||||
continue;
|
continue;
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = classInfo.load();
|
Class<?> clazz = classInfo.load();
|
||||||
if(packetClass.isAssignableFrom(clazz) && !clazz.isInterface() && ((clazz.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT)) {
|
if (packetClass.isAssignableFrom(clazz) && !clazz.isInterface() && ((clazz.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT)) {
|
||||||
allTests.add(DynamicTest.dynamicTest("WriteThenRead "+clazz.getSimpleName(), () -> {
|
allTests.add(DynamicTest.dynamicTest("WriteThenRead " + clazz.getSimpleName(), () -> {
|
||||||
// required for managers to be loaded
|
// required for managers to be loaded
|
||||||
MinecraftServer.init();
|
MinecraftServer.init();
|
||||||
|
|
||||||
@ -63,14 +63,13 @@ public class ReadWritePackets {
|
|||||||
T packet;
|
T packet;
|
||||||
|
|
||||||
// exceptions
|
// exceptions
|
||||||
if(clazz.getSimpleName().equals("EntityEquipmentPacket")) {
|
if (clazz.getSimpleName().equals("EntityEquipmentPacket")) {
|
||||||
// requires at least one slot and one item
|
// requires at least one slot and one item
|
||||||
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
||||||
p.itemStacks = new ItemStack[] { ItemStack.getAirItem() };
|
p.itemStacks = new ItemStack[]{ItemStack.AIR};
|
||||||
p.slots = new EntityEquipmentPacket.Slot[] { EntityEquipmentPacket.Slot.MAIN_HAND };
|
p.slots = new EntityEquipmentPacket.Slot[]{EntityEquipmentPacket.Slot.MAIN_HAND};
|
||||||
packet = (T) p;
|
packet = (T) p;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
packet = (T) constructor.newInstance();
|
packet = (T) constructor.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user