Refactor offline player data access
- Class that handle all Bukkit/NBT conversion of player data - Ability to read and save the player inventory (more to come later)
This commit is contained in:
parent
fb4c62a0bc
commit
bf59617e19
@ -2,19 +2,18 @@ package fr.pandacube.lib.paper.players;
|
|||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds;
|
import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftItemStack;
|
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
|
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.ListTag;
|
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.NbtIo;
|
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.NbtIo;
|
||||||
|
import fr.pandacube.lib.paper.util.PlayerDataWrapper;
|
||||||
import fr.pandacube.lib.paper.util.WorldUtil;
|
import fr.pandacube.lib.paper.util.WorldUtil;
|
||||||
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
|
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
import org.bukkit.scoreboard.Team;
|
import org.bukkit.scoreboard.Team;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -164,6 +163,16 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
|
|||||||
.getPlayerData(getUniqueId().toString());
|
.getPlayerData(getUniqueId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a wrapper for the NBT data from the playerdata file.
|
||||||
|
* It will not work if the player is online, because the data on the file are not synchronized with real-time values.
|
||||||
|
* @return the NBT data from the playerdata file.
|
||||||
|
* @throws IllegalStateException if the player is online.
|
||||||
|
*/
|
||||||
|
default PlayerDataWrapper getPlayerDataWrapper() {
|
||||||
|
return new PlayerDataWrapper(getPlayerData());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the provided NBT data to the playerdata file.
|
* Saves the provided NBT data to the playerdata file.
|
||||||
* It will not work if the player is online, because the provided data will be lost when the player disconnects.
|
* It will not work if the player is online, because the provided data will be lost when the player disconnects.
|
||||||
@ -171,14 +180,14 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
|
|||||||
* @throws IllegalStateException if the player is online.
|
* @throws IllegalStateException if the player is online.
|
||||||
* @throws IOException if an IO error occurs.
|
* @throws IOException if an IO error occurs.
|
||||||
*/
|
*/
|
||||||
default void savePlayerData(CompoundTag data) throws IOException {
|
default void savePlayerData(PlayerDataWrapper data) throws IOException {
|
||||||
if (isOnline())
|
if (isOnline())
|
||||||
throw new IllegalStateException("Cannot write data file of " + getName() + " because they’re online.");
|
throw new IllegalStateException("Cannot write data file of " + getName() + " because they’re online.");
|
||||||
File file = getPlayerDataFile(false);
|
File file = getPlayerDataFile(false);
|
||||||
File old = getPlayerDataFile(true);
|
File old = getPlayerDataFile(true);
|
||||||
old.delete();
|
old.delete();
|
||||||
Files.move(file, old);
|
Files.move(file, old);
|
||||||
NbtIo.writeCompressed(data, file);
|
NbtIo.writeCompressed(data.data, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,60 +201,19 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the content of the player’s inventory.
|
* Gets the player’s inventory.
|
||||||
* @return the content of the player’s inventory.
|
* @return the player’s inventory.
|
||||||
*/
|
*/
|
||||||
default ItemStack[] getInventoryContent() {
|
default PlayerInventory getInventory() {
|
||||||
ItemStack[] content = new ItemStack[InventoryType.PLAYER.getDefaultSize()];
|
return getPlayerDataWrapper().getInventory();
|
||||||
CompoundTag playerData = getPlayerData();
|
|
||||||
if (playerData == null)
|
|
||||||
return content;
|
|
||||||
ListTag nbttaglist = playerData.getList("Inventory", 10); // type of list element 10 is CompoundTag
|
|
||||||
if (nbttaglist == null)
|
|
||||||
return content;
|
|
||||||
// cat nbEl NBTslot bukkitSlot NBT->Bukkit
|
|
||||||
// items 36el 0-35 ==
|
|
||||||
// armor 4el start 100 36-39 -100 + 36
|
|
||||||
// offhnd 1el start 150 40 -150 + 40
|
|
||||||
for (int i = 0; i < nbttaglist.size(); i++) {
|
|
||||||
CompoundTag itemTag = nbttaglist.getCompound(i);
|
|
||||||
ItemStack is = CraftItemStack.asCraftMirror(itemTag);
|
|
||||||
if (is != null && !is.getType().isAir()) {
|
|
||||||
int nbtSlot = itemTag.getByte("Slot") & 255;
|
|
||||||
int bukkitSlot = nbtSlot < 36 ? nbtSlot
|
|
||||||
: (nbtSlot >= 100 && nbtSlot < 104) ? nbtSlot - 100 + 36
|
|
||||||
: nbtSlot == 150 ? 40
|
|
||||||
: -1;
|
|
||||||
if (bukkitSlot >= 0)
|
|
||||||
content[bukkitSlot] = is;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the content of the player’s enderchest.
|
* Gets the player’s enderchest.
|
||||||
* @return the content of the player’s enderchest.
|
* @return the player’s enderchest.
|
||||||
*/
|
*/
|
||||||
default ItemStack[] getEnderchestContent() {
|
default Inventory getEnderChest() {
|
||||||
ItemStack[] content = new ItemStack[InventoryType.ENDER_CHEST.getDefaultSize()];
|
return getPlayerDataWrapper().getEnderChest();
|
||||||
CompoundTag playerData = getPlayerData();
|
|
||||||
if (playerData == null || !playerData.contains("EnderItems", 9)) // type 9 is list
|
|
||||||
return content;
|
|
||||||
ListTag nbtList = playerData.getList("EnderItems", 10); // type of list element 10 is CompoundTag
|
|
||||||
if (nbtList == null)
|
|
||||||
return content;
|
|
||||||
for (int i = 0; i < nbtList.size(); i++) {
|
|
||||||
CompoundTag itemTag = nbtList.getCompound(i);
|
|
||||||
int nbtSlot = itemTag.getByte("Slot") & 255;
|
|
||||||
ItemStack is = CraftItemStack.asCraftMirror(itemTag);
|
|
||||||
if (nbtSlot < content.length && is != null && !is.getType().isAir()) {
|
|
||||||
content[nbtSlot] = CraftItemStack.asCraftMirror(itemTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import com.destroystokyo.paper.ClientOption.ChatVisibility;
|
|||||||
import com.destroystokyo.paper.SkinParts;
|
import com.destroystokyo.paper.SkinParts;
|
||||||
import fr.pandacube.lib.paper.players.PlayerNonPersistentConfig.Expiration;
|
import fr.pandacube.lib.paper.players.PlayerNonPersistentConfig.Expiration;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftPlayer;
|
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftPlayer;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
|
||||||
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
|
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
||||||
import net.kyori.adventure.audience.MessageType;
|
import net.kyori.adventure.audience.MessageType;
|
||||||
@ -20,8 +19,9 @@ import org.bukkit.Location;
|
|||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.MainHand;
|
import org.bukkit.inventory.MainHand;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -324,20 +324,13 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default CompoundTag getPlayerData() {
|
default PlayerInventory getInventory() {
|
||||||
CompoundTag tag = new CompoundTag();
|
return getBukkitPlayer().getInventory();
|
||||||
getWrappedCraftPlayer().getHandle().serializeEntity(tag);
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default ItemStack[] getInventoryContent() {
|
default Inventory getEnderChest() {
|
||||||
return getBukkitPlayer().getInventory().getContents();
|
return getBukkitPlayer().getEnderChest();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default ItemStack[] getEnderchestContent() {
|
|
||||||
return getBukkitPlayer().getEnderChest().getContents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
|
|||||||
public class CraftItemStack extends ReflectWrapperTyped<ItemStack> {
|
public class CraftItemStack extends ReflectWrapperTyped<ItemStack> {
|
||||||
public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("inventory.CraftItemStack"));
|
public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("inventory.CraftItemStack"));
|
||||||
public static final ReflectMethod<?> asCraftMirror = wrapEx(() -> REFLECT.method("asCraftMirror", fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.MAPPING.runtimeClass()));
|
public static final ReflectMethod<?> asCraftMirror = wrapEx(() -> REFLECT.method("asCraftMirror", fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.MAPPING.runtimeClass()));
|
||||||
|
public static final ReflectMethod<?> asNMSCopy = wrapEx(() -> REFLECT.method("asNMSCopy", ItemStack.class));
|
||||||
|
|
||||||
public static ItemStack asCraftMirror(fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack original) {
|
public static ItemStack asCraftMirror(fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack original) {
|
||||||
return (ItemStack) wrapReflectEx(() -> asCraftMirror.invokeStatic(unwrap(original)));
|
return (ItemStack) wrapReflectEx(() -> asCraftMirror.invokeStatic(unwrap(original)));
|
||||||
@ -28,6 +29,11 @@ public class CraftItemStack extends ReflectWrapperTyped<ItemStack> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack asNMSCopy(ItemStack original) {
|
||||||
|
return wrap(wrapReflectEx(() -> asNMSCopy.invokeStatic(original)), fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected CraftItemStack(Object obj) {
|
protected CraftItemStack(Object obj) {
|
||||||
|
@ -2,7 +2,6 @@ package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt;
|
|||||||
|
|
||||||
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
||||||
import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping;
|
import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
|
||||||
|
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
@ -17,6 +16,22 @@ public class CollectionTag extends ReflectWrapperTyped<AbstractList<?>> implemen
|
|||||||
return __getRuntimeInstance().size();
|
return __getRuntimeInstance().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Tag get(int i) {
|
||||||
|
return wrap(__getRuntimeInstance().get(i), Tag.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag set(int i, Tag t) {
|
||||||
|
return wrap(((AbstractList<Object>)__getRuntimeInstance()).set(i, unwrap(t)), Tag.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int i, Tag t) {
|
||||||
|
((AbstractList<Object>)__getRuntimeInstance()).add(i, unwrap(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag remove(int i) {
|
||||||
|
return wrap(__getRuntimeInstance().remove(i), Tag.class);
|
||||||
|
}
|
||||||
|
|
||||||
protected CollectionTag(Object nms) {
|
protected CollectionTag(Object nms) {
|
||||||
super(nms);
|
super(nms);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt;
|
|||||||
|
|
||||||
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
||||||
import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping;
|
import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping;
|
||||||
|
import fr.pandacube.lib.reflect.ReflectConstructor;
|
||||||
import fr.pandacube.lib.reflect.ReflectMethod;
|
import fr.pandacube.lib.reflect.ReflectMethod;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
||||||
|
|
||||||
@ -10,12 +11,17 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
|
|||||||
|
|
||||||
public class ListTag extends CollectionTag {
|
public class ListTag extends CollectionTag {
|
||||||
public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.ListTag"));
|
public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.ListTag"));
|
||||||
|
public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor());
|
||||||
private static final ReflectMethod<?> getCompound = wrapEx(() -> MAPPING.mojMethod("getCompound", int.class));
|
private static final ReflectMethod<?> getCompound = wrapEx(() -> MAPPING.mojMethod("getCompound", int.class));
|
||||||
|
|
||||||
public CompoundTag getCompound(int index) {
|
public CompoundTag getCompound(int index) {
|
||||||
return wrap(wrapReflectEx(() -> getCompound.invoke(__getRuntimeInstance(), index)), CompoundTag.class);
|
return wrap(wrapReflectEx(() -> getCompound.invoke(__getRuntimeInstance(), index)), CompoundTag.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListTag() {
|
||||||
|
this(wrapReflectEx(() -> CONSTRUCTOR.instanciate()));
|
||||||
|
}
|
||||||
|
|
||||||
protected ListTag(Object nms) {
|
protected ListTag(Object nms) {
|
||||||
super(nms);
|
super(nms);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
|
|||||||
|
|
||||||
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
import fr.pandacube.lib.paper.reflect.NMSReflect;
|
||||||
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
||||||
import fr.pandacube.lib.reflect.ReflectConstructor;
|
|
||||||
import fr.pandacube.lib.reflect.ReflectMethod;
|
import fr.pandacube.lib.reflect.ReflectMethod;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
||||||
|
|
||||||
@ -12,12 +11,19 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
|
|||||||
public class ItemStack extends ReflectWrapper {
|
public class ItemStack extends ReflectWrapper {
|
||||||
public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.world.item.ItemStack"));
|
public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.world.item.ItemStack"));
|
||||||
private static final ReflectMethod<?> of = wrapEx(() -> MAPPING.mojMethod("of", CompoundTag.MAPPING));
|
private static final ReflectMethod<?> of = wrapEx(() -> MAPPING.mojMethod("of", CompoundTag.MAPPING));
|
||||||
|
private static final ReflectMethod<?> save = wrapEx(() -> MAPPING.mojMethod("save", CompoundTag.MAPPING));
|
||||||
|
|
||||||
public static ItemStack of(CompoundTag nbt) {
|
public static ItemStack of(CompoundTag nbt) {
|
||||||
return wrap(wrapReflectEx(() -> of.invokeStatic(unwrap(nbt))), ItemStack.class);
|
return wrap(wrapReflectEx(() -> of.invokeStatic(unwrap(nbt))), ItemStack.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected ItemStack(Object obj) {
|
protected ItemStack(Object obj) {
|
||||||
super(obj);
|
super(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CompoundTag save(CompoundTag nbt) {
|
||||||
|
return wrap(wrapReflectEx(() -> save.invoke(__getRuntimeInstance(), unwrap(nbt))), CompoundTag.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
package fr.pandacube.lib.paper.util;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.HumanEntity;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
public class InventoryWrapper implements Inventory {
|
||||||
|
private final Inventory base;
|
||||||
|
|
||||||
|
public InventoryWrapper(Inventory base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return base.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxStackSize() {
|
||||||
|
return base.getMaxStackSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaxStackSize(int size) {
|
||||||
|
base.setMaxStackSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack getItem(int index) {
|
||||||
|
return base.getItem(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(int index, @Nullable ItemStack item) {
|
||||||
|
base.setItem(index, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||||
|
return base.addItem(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||||
|
return base.removeItem(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HashMap<Integer, ItemStack> removeItemAnySlot(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||||
|
return base.removeItemAnySlot(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack @NotNull [] getContents() {
|
||||||
|
return base.getContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContents(@Nullable ItemStack @NotNull [] items) throws IllegalArgumentException {
|
||||||
|
base.setContents(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack @NotNull [] getStorageContents() {
|
||||||
|
return base.getStorageContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStorageContents(@Nullable ItemStack @NotNull [] items) throws IllegalArgumentException {
|
||||||
|
base.setStorageContents(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
|
||||||
|
return base.contains(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@Nullable ItemStack item) {
|
||||||
|
return base.contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
|
||||||
|
return base.contains(material, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@Nullable ItemStack item, int amount) {
|
||||||
|
return base.contains(item, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
|
||||||
|
return base.containsAtLeast(item, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HashMap<Integer, ? extends ItemStack> all(@NotNull Material material) throws IllegalArgumentException {
|
||||||
|
return base.all(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HashMap<Integer, ? extends ItemStack> all(@Nullable ItemStack item) {
|
||||||
|
return base.all(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int first(@NotNull Material material) throws IllegalArgumentException {
|
||||||
|
return base.first(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int first(@NotNull ItemStack item) {
|
||||||
|
return base.first(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int firstEmpty() {
|
||||||
|
return base.firstEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return base.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull Material material) throws IllegalArgumentException {
|
||||||
|
base.remove(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull ItemStack item) {
|
||||||
|
base.remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear(int index) {
|
||||||
|
base.clear(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
base.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int close() {
|
||||||
|
return base.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<HumanEntity> getViewers() {
|
||||||
|
return base.getViewers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull InventoryType getType() {
|
||||||
|
return base.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable InventoryHolder getHolder() {
|
||||||
|
return base.getHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable InventoryHolder getHolder(boolean useSnapshot) {
|
||||||
|
return base.getHolder(useSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ListIterator<ItemStack> iterator() {
|
||||||
|
return base.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ListIterator<ItemStack> iterator(int index) {
|
||||||
|
return base.iterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Location getLocation() {
|
||||||
|
return base.getLocation();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,350 @@
|
|||||||
|
package fr.pandacube.lib.paper.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftItemStack;
|
||||||
|
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
|
||||||
|
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.ListTag;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.HumanEntity;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper to easily manipulate vanilla player data.
|
||||||
|
*/
|
||||||
|
public class PlayerDataWrapper {
|
||||||
|
|
||||||
|
public final CompoundTag data;
|
||||||
|
|
||||||
|
public PlayerDataWrapper(CompoundTag data) {
|
||||||
|
this.data = data == null ? new CompoundTag() : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a snapshot of the inventory of this player.
|
||||||
|
* If modified, call the {@link #setInventory(PlayerInventory)} to update the data.
|
||||||
|
* @return the player inventory
|
||||||
|
*/
|
||||||
|
public PlayerInventory getInventory() {
|
||||||
|
return new DummyPlayerInventory(
|
||||||
|
getBukkitInventory("Inventory", InventoryType.PLAYER, this::fromNBTtoBukkitInventorySlot),
|
||||||
|
getHeldItemSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fromNBTtoBukkitInventorySlot(int nbtSlot) {
|
||||||
|
// cat nbEl NBTslot bukkitSlot NBT->Bukkit
|
||||||
|
// items 36el 0-35 ==
|
||||||
|
// armor 4el start 100 36-39 -100 + 36
|
||||||
|
// offhnd 1el start 150 40 -150 + 40
|
||||||
|
if (nbtSlot >= 0 && nbtSlot < 36) { // regular inventory slots
|
||||||
|
return nbtSlot;
|
||||||
|
}
|
||||||
|
if (nbtSlot >= 100 && nbtSlot < 104) { // armor slots
|
||||||
|
return nbtSlot - 100 + 36;
|
||||||
|
}
|
||||||
|
if (nbtSlot == 150) { // second hand
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unrecognized NBT player inventory slot " + nbtSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInventory(PlayerInventory inv) {
|
||||||
|
setBukkitInventory("Inventory", inv, this::fromBukkitToNBTInventorySlot);
|
||||||
|
setHeldItemSlot(inv.getHeldItemSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fromBukkitToNBTInventorySlot(int bukkitSlot) {
|
||||||
|
if (bukkitSlot >= 0 && bukkitSlot < 36) { // regular inventory slots
|
||||||
|
return bukkitSlot;
|
||||||
|
}
|
||||||
|
if (bukkitSlot >= 36 && bukkitSlot < 40) { // armor slots
|
||||||
|
return bukkitSlot + 100 - 36;
|
||||||
|
}
|
||||||
|
if (bukkitSlot == 40) { // second hand
|
||||||
|
return 150;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unrecognized Bukkit player inventory slot " + bukkitSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Inventory getEnderChest() {
|
||||||
|
return getBukkitInventory("EnderItems", InventoryType.ENDER_CHEST, IntUnaryOperator.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnderChest(Inventory inv) {
|
||||||
|
setBukkitInventory("EnderItems", inv, IntUnaryOperator.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private int getHeldItemSlot() {
|
||||||
|
if (!data.contains("SelectedItemSlot"))
|
||||||
|
return 0;
|
||||||
|
return data.getInt("SelectedItemSlot");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setHeldItemSlot(int slot) {
|
||||||
|
data.putInt("SelectedItemSlot", slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Inventory getBukkitInventory(String nbtKey, InventoryType bukkitType, IntUnaryOperator nbtToBukkitSlotConverter) {
|
||||||
|
Map<Integer, ItemStack> stacks = getRawInvontoryContent(nbtKey);
|
||||||
|
Inventory inv = Bukkit.createInventory(null, bukkitType);
|
||||||
|
if (stacks.isEmpty())
|
||||||
|
return inv;
|
||||||
|
for (Map.Entry<Integer, ItemStack> is : stacks.entrySet()) {
|
||||||
|
inv.setItem(nbtToBukkitSlotConverter.applyAsInt(is.getKey()), is.getValue());
|
||||||
|
}
|
||||||
|
return inv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, ItemStack> getRawInvontoryContent(String key) {
|
||||||
|
if (!data.contains(key, 9)) // type 9 is list
|
||||||
|
return Map.of();
|
||||||
|
ListTag list = data.getList(key, 10); // type of list element 10 is CompoundTag
|
||||||
|
if (list == null)
|
||||||
|
return Map.of();
|
||||||
|
|
||||||
|
Map<Integer, ItemStack> stacks = new TreeMap<>();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
CompoundTag itemTag = list.getCompound(i);
|
||||||
|
int nbtSlot = itemTag.getByte("Slot") & 255;
|
||||||
|
ItemStack is = CraftItemStack.asCraftMirror(itemTag);
|
||||||
|
if (is != null && !is.getType().isAir()) {
|
||||||
|
stacks.put(nbtSlot, CraftItemStack.asCraftMirror(itemTag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void setBukkitInventory(String nbtKey, Inventory inv, IntUnaryOperator bukkitToNBTSlotconverter) {
|
||||||
|
Map<Integer, ItemStack> stacks = new TreeMap<>();
|
||||||
|
if (inv == null) {
|
||||||
|
setRawInventoryContent(nbtKey, stacks);
|
||||||
|
}
|
||||||
|
for (int bukkitSlot = 0; bukkitSlot < inv.getSize(); bukkitSlot++) {
|
||||||
|
ItemStack is = inv.getItem(bukkitSlot);
|
||||||
|
int nbtSlot = bukkitToNBTSlotconverter.applyAsInt(bukkitSlot);
|
||||||
|
stacks.put(nbtSlot, is);
|
||||||
|
}
|
||||||
|
setRawInventoryContent(nbtKey, stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRawInventoryContent(String key, Map<Integer, ItemStack> stacks) {
|
||||||
|
ListTag list = new ListTag();
|
||||||
|
for (Map.Entry<Integer, ItemStack> is : stacks.entrySet()) {
|
||||||
|
CompoundTag itemTag = new CompoundTag();
|
||||||
|
CraftItemStack.asNMSCopy(is.getValue()).save(itemTag);
|
||||||
|
itemTag.putByte("Slot", is.getKey().byteValue());
|
||||||
|
list.add(list.size(), itemTag);
|
||||||
|
}
|
||||||
|
data.put(key, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static class DummyPlayerInventory extends InventoryWrapper implements PlayerInventory {
|
||||||
|
|
||||||
|
private int heldItemSlot;
|
||||||
|
|
||||||
|
public DummyPlayerInventory(Inventory base, int heldItemSlot) {
|
||||||
|
super(base);
|
||||||
|
this.heldItemSlot = heldItemSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack @NotNull [] getStorageContents() {
|
||||||
|
return Arrays.copyOfRange(getContents(), 0, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack @NotNull [] getArmorContents() {
|
||||||
|
return Arrays.copyOfRange(getContents(), 36, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||||
|
this.setSlots(items, 36, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStack @NotNull [] getExtraContents() {
|
||||||
|
return Arrays.copyOfRange(getContents(), 40, getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||||
|
this.setSlots(items, 40, getSize() - 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSlots(ItemStack[] items, int baseSlot, int length) {
|
||||||
|
if (items == null) {
|
||||||
|
items = new ItemStack[length];
|
||||||
|
}
|
||||||
|
Preconditions.checkArgument(items.length <= length, "items.length must be < %s", length);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (i >= items.length) {
|
||||||
|
this.setItem(baseSlot + i, null);
|
||||||
|
} else {
|
||||||
|
this.setItem(baseSlot + i, items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getHelmet() {
|
||||||
|
return getItem(39);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHelmet(@Nullable ItemStack helmet) {
|
||||||
|
setItem(39, helmet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getChestplate() {
|
||||||
|
return getItem(38);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||||
|
setItem(38, chestplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getLeggings() {
|
||||||
|
return getItem(37);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLeggings(@Nullable ItemStack leggings) {
|
||||||
|
setItem(37, leggings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getBoots() {
|
||||||
|
return getItem(36);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBoots(@Nullable ItemStack boots) {
|
||||||
|
setItem(36, boots);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(EquipmentSlot slot, ItemStack item) {
|
||||||
|
Preconditions.checkArgument(slot != null, "slot must not be null");
|
||||||
|
|
||||||
|
switch (slot) {
|
||||||
|
case HAND:
|
||||||
|
this.setItemInMainHand(item);
|
||||||
|
break;
|
||||||
|
case OFF_HAND:
|
||||||
|
this.setItemInOffHand(item);
|
||||||
|
break;
|
||||||
|
case FEET:
|
||||||
|
this.setBoots(item);
|
||||||
|
break;
|
||||||
|
case LEGS:
|
||||||
|
this.setLeggings(item);
|
||||||
|
break;
|
||||||
|
case CHEST:
|
||||||
|
this.setChestplate(item);
|
||||||
|
break;
|
||||||
|
case HEAD:
|
||||||
|
this.setHelmet(item);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Not implemented. This is a bug");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getItem(EquipmentSlot slot) {
|
||||||
|
Preconditions.checkArgument(slot != null, "slot must not be null");
|
||||||
|
|
||||||
|
return switch (slot) {
|
||||||
|
case HAND -> this.getItemInMainHand();
|
||||||
|
case OFF_HAND -> this.getItemInOffHand();
|
||||||
|
case FEET -> Objects.requireNonNullElseGet(this.getBoots(), () -> new ItemStack(Material.AIR));
|
||||||
|
case LEGS -> Objects.requireNonNullElseGet(this.getLeggings(), () -> new ItemStack(Material.AIR));
|
||||||
|
case CHEST -> Objects.requireNonNullElseGet(this.getChestplate(), () -> new ItemStack(Material.AIR));
|
||||||
|
case HEAD -> Objects.requireNonNullElseGet(this.getHelmet(), () -> new ItemStack(Material.AIR));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ItemStack getItemInMainHand() {
|
||||||
|
return getItem(heldItemSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||||
|
setItem(heldItemSlot, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ItemStack getItemInOffHand() {
|
||||||
|
return getItem(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||||
|
setItem(40, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ItemStack getItemInHand() {
|
||||||
|
return getItemInMainHand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItemInHand(@Nullable ItemStack stack) {
|
||||||
|
setItemInMainHand(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeldItemSlot() {
|
||||||
|
return heldItemSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHeldItemSlot(int slot) {
|
||||||
|
if (slot < 0 || slot > 8)
|
||||||
|
throw new IllegalArgumentException("Slot is not between 0 and 8 inclusive");
|
||||||
|
heldItemSlot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable HumanEntity getHolder() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user