Merge pull request #217 from RinesThaix/inventoryImprovementsWithNewItemAPI

InventoryModifier to AbstractInventory and additional methods for inventories
This commit is contained in:
TheMode 2021-04-04 03:09:27 +02:00 committed by GitHub
commit 1c9528908c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 621 additions and 180 deletions

View File

@ -6,8 +6,9 @@ import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.type.decoration.EntityItemFrame;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.item.metadata.MapMeta;
import net.minestom.server.item.meta.MapMeta;
import net.minestom.server.map.Framebuffer;
import net.minestom.server.map.LargeFramebuffer;
import net.minestom.server.map.MapColors;
@ -67,8 +68,9 @@ public class Demo {
private static void createFrame(Instance instance, int id, int x, int y, int z) {
EntityItemFrame itemFrame = new EntityItemFrame(new Position(x, y, z), EntityItemFrame.ItemFrameOrientation.NORTH);
itemFrame.getPosition().setYaw(180f);
ItemStack map = new ItemStack(Material.FILLED_MAP, (byte) 1);
map.setItemMeta(new MapMeta(id));
ItemStack map = ItemStack.builder(Material.FILLED_MAP)
.meta(new MapMeta.Builder().mapId(id).build())
.build();
itemFrame.setItemStack(map);
itemFrame.setInstance(instance);
itemFrame.setCustomNameVisible(true);

View File

@ -4,7 +4,7 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryModifier;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.client.ClientPlayPacket;
@ -48,12 +48,12 @@ public class FakePlayerController {
*/
public void clickWindow(boolean playerInventory, short slot, byte button, short action, int mode) {
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
InventoryModifier inventoryModifier = inventory == null ? fakePlayer.getInventory() : inventory;
playerInventory = inventoryModifier instanceof PlayerInventory;
AbstractInventory abstractInventory = inventory == null ? fakePlayer.getInventory() : inventory;
playerInventory = abstractInventory instanceof PlayerInventory;
slot = playerInventory ? (short) PlayerInventoryUtils.convertToPacketSlot(slot) : slot;
ItemStack itemStack = inventoryModifier.getItemStack(slot);
ItemStack itemStack = abstractInventory.getItemStack(slot);
ClientClickWindowPacket clickWindowPacket = new ClientClickWindowPacket();
clickWindowPacket.windowId = playerInventory ? 0 : inventory.getWindowId();

View File

@ -0,0 +1,316 @@
package net.minestom.server.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.inventory.condition.InventoryCondition;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.StackingRule;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.UnaryOperator;
/**
* Represents an inventory where items can be modified/retrieved.
*/
public abstract class AbstractInventory {
/**
* 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 abstract void setItemStack(int slot, @NotNull ItemStack itemStack);
protected abstract void safeItemInsert(int slot, @NotNull ItemStack itemStack);
/**
* Adds an {@link ItemStack} to the inventory and sends 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
*/
public boolean addItemStack(@NotNull ItemStack itemStack) {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = 0; i < getInnerSize(); i++) {
ItemStack inventoryItem = 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, inventoryItem.withAmount(totalAmount));
itemStack = ItemStack.AIR;
break;
}
}
}
for (int i = 0; i < getInnerSize(); i++) {
ItemStack inventoryItem = getItemStack(i);
if (!inventoryItem.isAir()) {
continue;
}
// Fill the slot
itemChangesMap.put(i, itemStack);
itemStack = ItemStack.AIR;
break;
}
if (itemStack.isAir()) {
// Item can be fully placed inside the inventory, do so
itemChangesMap.forEach(this::safeItemInsert);
return true;
} else {
// Inventory cannot accept the item fully
return false;
}
}
/**
* Adds {@link ItemStack}s to the inventory and sends relevant updates to the viewer(s).
* <p>
* Even items cannot be fully added, the amount of {@code itemStack}s will be updated.
*
* @param itemStacks items to add
* @return list of itemstacks that could not be successfully fully added, empty list otherwise
*/
public List<ItemStack> addItemStacks(@NotNull List<ItemStack> itemStacks) {
List<ItemStack> result = new ArrayList<>();
itemStacks.forEach(itemStack -> {
if (!addItemStack(itemStack)) {
result.add(itemStack);
}
});
return result;
}
/**
* Checks whether {@link ItemStack} can be fully added to the inventory.
*
* @param itemStack the item to be checked
* @return true if the item can be fully added to the inventory, false otherwise
*/
public boolean canAddItemStack(@NotNull ItemStack itemStack) {
final StackingRule stackingRule = itemStack.getStackingRule();
int amountLeft = itemStack.getAmount();
for (int i = 0; i < getInnerSize(); i++) {
ItemStack inventoryItem = getItemStack(i);
if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
final int itemAmount = stackingRule.getAmount(inventoryItem);
if (itemAmount == stackingRule.getMaxSize())
continue;
if (!stackingRule.canApply(itemStack, amountLeft + itemAmount)) {
// Slot cannot accept the whole item, reduce amount to 'itemStack'
amountLeft -= stackingRule.getMaxSize() - itemAmount;
} else {
return true;
}
} else if (inventoryItem.isAir()) {
return true;
}
}
return false;
}
/**
* Checks whether {@link ItemStack}s can be fully added to the inventory.
*
* @param itemStacks items to be checked
* @return true if all the items can be fully added to the inventory, false otherwise
*/
public boolean canAddItemStacks(@NotNull List<ItemStack> itemStacks) {
return itemStacks.stream().allMatch(this::canAddItemStack);
}
/**
* Takes an {@link ItemStack} from the inventory and sends relevant update to the viewer(s).
* <p>
* Even the item cannot be fully taken, the amount of {@code itemStack} will be updated.
*
* @param itemStack the item to take
* @return true if the item has been successfully fully taken, false otherwise
*/
public boolean takeItemStack(@NotNull ItemStack itemStack) {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = 0; i < getInnerSize(); i++) {
ItemStack inventoryItem = 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 = ItemStack.AIR;
break;
}
itemChangesMap.put(i, ItemStack.AIR);
itemStack = itemStack.withAmount(amount -> amount - itemAmount);
if (itemStack.getAmount() == 0) {
itemStack = ItemStack.AIR;
break;
}
}
}
if (itemStack.isAir()) {
// Item can be fully taken from the inventory, do so
itemChangesMap.forEach(this::safeItemInsert);
return true;
} else {
return false;
}
}
/**
* Takes {@link ItemStack}s from the inventory and sends relevant updates to the viewer(s).
* <p>
* Even items cannot be fully taken, the amount of {@code itemStack}s will be updated.
*
* @param itemStacks items to take
* @return list of itemstacks that could not be successfully fully taken, empty list otherwise
*/
public List<ItemStack> takeItemStacks(@NotNull List<ItemStack> itemStacks) {
List<ItemStack> result = new ArrayList<>();
itemStacks.forEach(itemStack -> {
if (!takeItemStack(itemStack)) {
result.add(itemStack);
}
});
return result;
}
/**
* Checks whether {@link ItemStack} can be fully taken from the inventory.
*
* @param itemStack the item to be checked
* @return true if the item can be fully taken from the inventory, false otherwise
*/
public boolean canTakeItemStack(@NotNull ItemStack itemStack) {
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = 0; i < getInnerSize(); i++) {
ItemStack inventoryItem = 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) {
return true;
}
itemStack = itemStack.withAmount(amount -> amount - itemAmount);
}
}
return false;
}
/**
* Checks whether {@link ItemStack}s can be fully taken from the inventory.
*
* @param itemStacks items to be checked
* @return true if all the items can be fully taken from the inventory, false otherwise
*/
public boolean canTakeItemStacks(@NotNull List<ItemStack> itemStacks) {
return itemStacks.stream().allMatch(this::canTakeItemStack);
}
public 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 abstract 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
public abstract 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
public abstract ItemStack[] getItemStacks();
/**
* Gets the size of the inventory.
*
* @return the inventory's size
*/
public abstract int getSize();
/**
* 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
*/
@NotNull
public abstract List<InventoryCondition> getInventoryConditions();
/**
* Adds a new {@link InventoryCondition} to this inventory.
*
* @param inventoryCondition the inventory condition to add
*/
public abstract 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
*/
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);
}
}
}

View File

@ -37,7 +37,7 @@ import java.util.function.UnaryOperator;
* You can create one with {@link Inventory#Inventory(InventoryType, String)} or by making your own subclass.
* It can then be opened using {@link Player#openInventory(Inventory)}.
*/
public class Inventory implements InventoryModifier, InventoryClickHandler, Viewable, DataContainer {
public class Inventory extends AbstractInventory implements InventoryClickHandler, Viewable, DataContainer {
// incremented each time an inventory is created (used in the window packets)
private static final AtomicInteger LAST_INVENTORY_ID = new AtomicInteger();
@ -150,19 +150,13 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
@Override
public synchronized boolean addItemStack(@NotNull ItemStack itemStack) {
// Make the method synchronized
return InventoryModifier.super.addItemStack(itemStack);
}
@Override
public synchronized boolean addItemStack(@NotNull ItemStack itemStack, int startSlot, int endSlot) {
// Make the method synchronized
return InventoryModifier.super.addItemStack(itemStack, startSlot, endSlot);
return super.addItemStack(itemStack);
}
@Override
public synchronized void replaceItemStack(int slot, @NotNull UnaryOperator<@NotNull ItemStack> operator) {
// Make the method synchronized
InventoryModifier.super.replaceItemStack(slot, operator);
super.replaceItemStack(slot, operator);
}
@Override
@ -299,7 +293,8 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
* @param slot the internal slot id
* @param itemStack the item to insert
*/
private synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
@Override
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
this.itemStacks[slot] = itemStack;
SetSlotPacket setSlotPacket = new SetSlotPacket();
setSlotPacket.windowId = getWindowId();

View File

@ -1,145 +0,0 @@
package net.minestom.server.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.inventory.condition.InventoryCondition;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.StackingRule;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.UnaryOperator;
/**
* 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
*/
default boolean addItemStack(@NotNull ItemStack itemStack) {
return addItemStack(itemStack, 0, getSize());
}
default boolean addItemStack(@NotNull ItemStack itemStack, int startSlot, int endSlot) {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = startSlot; i < endSlot; i++) {
ItemStack inventoryItem = getItemStack(i);
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, inventoryItem.withAmount(totalAmount));
itemStack = ItemStack.AIR;
break;
}
} else if (inventoryItem.isAir()) {
// Fill the slot
itemChangesMap.put(i, itemStack);
itemStack = ItemStack.AIR;
break;
}
}
if (itemStack.isAir()) {
// Item can be fully placed inside the inventory, do so
itemChangesMap.forEach(this::setItemStack);
return true;
} else {
// Inventory cannot accept the item fully
return false;
}
}
default 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).
*/
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);
}
}
}

View File

@ -30,9 +30,10 @@ import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
/**
* Represents the inventory of a {@link Player}, retrieved with {@link Player#getInventory()}.
*/
public class PlayerInventory implements InventoryModifier, InventoryClickHandler, EquipmentHandler, DataContainer {
public class PlayerInventory extends AbstractInventory implements InventoryClickHandler, EquipmentHandler, DataContainer {
public static final int INVENTORY_SIZE = 46;
public static final int INNER_INVENTORY_SIZE = 36;
protected final Player player;
protected final ItemStack[] itemStacks = new ItemStack[INVENTORY_SIZE];
@ -93,24 +94,13 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return false;
itemStack = addItemStackEvent.getItemStack();
return InventoryModifier.super.addItemStack(itemStack, 0, getSize() - 10);
}
@Override
public synchronized boolean addItemStack(@NotNull ItemStack itemStack, int startSlot, int endSlot) {
PlayerAddItemStackEvent addItemStackEvent = new PlayerAddItemStackEvent(player, itemStack);
player.callEvent(PlayerAddItemStackEvent.class, addItemStackEvent);
if (addItemStackEvent.isCancelled())
return false;
itemStack = addItemStackEvent.getItemStack();
return InventoryModifier.super.addItemStack(itemStack, startSlot, endSlot);
return super.addItemStack(itemStack);
}
@Override
public synchronized void replaceItemStack(int slot, @NotNull UnaryOperator<@NotNull ItemStack> operator) {
// Make the method synchronized
InventoryModifier.super.replaceItemStack(slot, operator);
super.replaceItemStack(slot, operator);
}
@Override
@ -130,6 +120,11 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return INVENTORY_SIZE;
}
@Override
public int getInnerSize() {
return INNER_INVENTORY_SIZE;
}
@NotNull
@Override
public ItemStack getItemInMainHand() {
@ -237,6 +232,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
* @throws IllegalArgumentException if the slot {@code slot} does not exist
* @throws NullPointerException if {@code itemStack} is null
*/
@Override
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
"The slot " + slot + " does not exist for player");

View File

@ -1,11 +1,11 @@
package net.minestom.server.inventory.condition;
import net.minestom.server.entity.Player;
import net.minestom.server.inventory.InventoryModifier;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.click.ClickType;
/**
* Can be added to any {@link InventoryModifier}
* Can be added to any {@link AbstractInventory}
* using {@link net.minestom.server.inventory.Inventory#addInventoryCondition(InventoryCondition)}
* or {@link net.minestom.server.inventory.PlayerInventory#addInventoryCondition(InventoryCondition)}
* in order to listen to any issued clicks.

View File

@ -0,0 +1,278 @@
package net.minestom.server.item.meta;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.item.ItemMeta;
import net.minestom.server.item.ItemMetaBuilder;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
public class MapMeta extends ItemMeta {
private final int mapId;
private final int mapScaleDirection;
private final List<MapDecoration> decorations;
private final Color mapColor;
protected MapMeta(ItemMetaBuilder metaBuilder,
int mapId,
int mapScaleDirection,
@NotNull List<MapDecoration> decorations,
@NotNull Color mapColor) {
super(metaBuilder);
this.mapId = mapId;
this.mapScaleDirection = mapScaleDirection;
this.decorations = decorations;
this.mapColor = mapColor;
}
/**
* Gets the map id.
*
* @return the map id
*/
public int getMapId() {
return mapId;
}
/**
* Gets the map scale direction.
*
* @return the map scale direction
*/
public int getMapScaleDirection() {
return mapScaleDirection;
}
/**
* Gets the map decorations.
*
* @return a modifiable list containing all the map decorations
*/
public List<MapDecoration> getDecorations() {
return decorations;
}
/**
* Gets the map color.
*
* @return the map color
* @deprecated Use {@link #getMapColor()}
*/
@Deprecated
public ChatColor getLegacyMapColor() {
return this.mapColor.asLegacyChatColor();
}
/**
* Gets the map color.
*
* @return the map color
*/
public @NotNull Color getMapColor() {
return this.mapColor;
}
public static class Builder extends ItemMetaBuilder {
private int mapId;
private int mapScaleDirection = 1;
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
private Color mapColor = new Color(0, 0, 0);
public Builder mapId(int value) {
this.mapId = value;
return this;
}
public Builder mapScaleDirection(int value) {
this.mapScaleDirection = value;
return this;
}
public Builder decorations(List<MapDecoration> value) {
this.decorations = value;
return this;
}
public Builder mapColor(Color value) {
this.mapColor = value;
return this;
}
@Override
public @NotNull ItemMeta build() {
return new MapMeta(this, mapId, mapScaleDirection, decorations, mapColor);
}
@Override
public void read(@NotNull NBTCompound compound) {
if (compound.containsKey("map")) {
this.mapId = compound.getAsInt("map");
}
if (compound.containsKey("map_scale_direction")) {
this.mapScaleDirection = compound.getAsInt("map_scale_direction");
}
if (compound.containsKey("Decorations")) {
final NBTList<NBTCompound> decorationsList = compound.getList("Decorations");
for (NBTCompound decorationCompound : decorationsList) {
final String id = decorationCompound.getString("id");
final byte type = decorationCompound.getAsByte("type");
byte x = 0;
if (decorationCompound.containsKey("x")) {
x = decorationCompound.getAsByte("x");
}
byte z = 0;
if (decorationCompound.containsKey("z")) {
z = decorationCompound.getAsByte("z");
}
double rotation = 0.0;
if (decorationCompound.containsKey("rot")) {
rotation = decorationCompound.getAsDouble("rot");
}
this.decorations.add(new MapDecoration(id, type, x, z, rotation));
}
}
if (compound.containsKey("display")) {
final NBTCompound displayCompound = compound.getCompound("display");
if (displayCompound.containsKey("MapColor")) {
this.mapColor = new Color(displayCompound.getAsInt("MapColor"));
}
}
}
@Override
public void write(@NotNull NBTCompound compound) {
compound.setInt("map", mapId);
compound.setInt("map_scale_direction", mapScaleDirection);
if (!decorations.isEmpty()) {
NBTList<NBTCompound> decorationsList = new NBTList<>(NBTTypes.TAG_Compound);
for (MapDecoration decoration : decorations) {
NBTCompound decorationCompound = new NBTCompound();
decorationCompound.setString("id", decoration.getId());
decorationCompound.setByte("type", decoration.getType());
decorationCompound.setByte("x", decoration.getX());
decorationCompound.setByte("z", decoration.getZ());
decorationCompound.setDouble("rot", decoration.getRotation());
decorationsList.add(decorationCompound);
}
compound.set("Decorations", decorationsList);
}
{
NBTCompound displayCompound;
if (compound.containsKey("display")) {
displayCompound = compound.getCompound("display");
} else {
displayCompound = new NBTCompound();
}
displayCompound.setInt("MapColor", mapColor.asRGB());
}
}
@Override
protected void deepClone(@NotNull ItemMetaBuilder metaBuilder) {
var mapBuilder = (MapMeta.Builder) metaBuilder;
mapBuilder.mapId = mapId;
mapBuilder.mapScaleDirection = mapScaleDirection;
mapBuilder.decorations = decorations;
mapBuilder.mapColor = mapColor;
}
@Override
protected @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier() {
return Builder::new;
}
}
public static class MapDecoration implements PublicCloneable<MapDecoration> {
private final String id;
private final byte type;
private final byte x, z;
private final double rotation;
public MapDecoration(@NotNull String id, byte type, byte x, byte z, double rotation) {
this.id = id;
this.type = type;
this.x = x;
this.z = z;
this.rotation = rotation;
}
/**
* Gets the arbitrary decoration id.
*
* @return the decoration id
*/
public String getId() {
return id;
}
/**
* Gets the decoration type.
*
* @return the decoration type
* @see <a href="https://minecraft.gamepedia.com/Map#Map_icons">Map icons</a>
*/
public byte getType() {
return type;
}
/**
* Gets the X position of the decoration.
*
* @return the X position
*/
public byte getX() {
return x;
}
/**
* Gets the Z position of the decoration.
*
* @return the Z position
*/
public byte getZ() {
return z;
}
/**
* Gets the rotation of the symbol (0;360).
*
* @return the rotation of the symbol
*/
public double getRotation() {
return rotation;
}
@NotNull
@Override
public MapDecoration clone() {
try {
return (MapDecoration) super.clone();
} catch (CloneNotSupportedException e) {
MinecraftServer.getExceptionManager().handleException(e);
throw new IllegalStateException("Something weird happened");
}
}
}
}

View File

@ -1,6 +1,7 @@
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;

View File

@ -1,10 +1,8 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.color.DyeColor;
import net.minestom.server.utils.clone.CloneUtils;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;