3
0
Mirror von https://github.com/St3venAU/ArmorStandTools.git synchronisiert 2024-12-28 04:20:08 +01:00

Prevent configured armor stand items and shulker boxes getting into a new configured armor stand item causing something like a book ban, properly escape armor stand name to prevent NBT tag injection

Dieser Commit ist enthalten in:
BuildTools 2023-02-04 12:58:16 +01:00
Ursprung 6acd7b52d0
Commit 0651dfdc18
5 geänderte Dateien mit 61 neuen und 24 gelöschten Zeilen

Datei anzeigen

@ -10,10 +10,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
@ -263,6 +260,11 @@ class ArmorStandGUI implements Listener {
break;
case ITEM:
ItemStack stack = Utils.createArmorStandItem(as);
if(stack == null){
p.sendMessage(ChatColor.RED + Config.armorStandItemFail);
p.closeInventory();
break;
}
HashMap<Integer, ItemStack> leftover = p.getInventory().addItem(stack);
if(leftover.containsKey(0)){ // if inventory full, drop item on ground and tell the player
p.getWorld().dropItem(as.getLocation(), leftover.get(0)).setVelocity(new Vector(0, 0.2d, 0));

Datei anzeigen

@ -74,7 +74,8 @@ class Config {
listAssignedCmds, addACmd, removeACmd, cmdHelp,
enterName, enterName2, enterSkull, inputTimeout,
nameSet, nameRemoved, skullSet, enterNameC,
enterNameC2, enterSkullC, invFullForArmorStandItem;
enterNameC2, enterSkullC, invFullForArmorStandItem,
armorStandItemFail;
static void reload() {
reloadMainConfig();
@ -262,6 +263,7 @@ class Config {
enterNameC2 = languageConfig.getString("enterNameC2");
enterSkullC = languageConfig.getString("enterSkullC");
invFullForArmorStandItem = languageConfig.getString("invFullForArmorStandItem");
armorStandItemFail = languageConfig.getString("armorStandItemFail");
}
private static ItemStack getItemStack(String configPath) {

Datei anzeigen

@ -5,11 +5,14 @@ import org.bukkit.inventory.ItemStack;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
public class ItemStackReflections {
private final static Method AS_NMS_COPY;
private final static Method GET_TAG;
private final static Method MODIFY_ITEM_STACK;
private final static Method CONTAINS;
private final static Object CRAFT_MAGIC_NUMBERS;
private static final String SERVER_VERSION;
@ -23,33 +26,45 @@ public class ItemStackReflections {
Class<?> mcItemStack = Class.forName("net.minecraft.world.item.ItemStack");
Class<?> bukkitItemStack = Class.forName("org.bukkit.craftbukkit." + SERVER_VERSION + ".inventory.CraftItemStack");
Class<?> craftMagicNumbers = Class.forName("org.bukkit.craftbukkit." + SERVER_VERSION + ".util.CraftMagicNumbers");
Class<?> nbtTagCompound = Class.forName("net.minecraft.nbt.NBTTagCompound");
AS_NMS_COPY = bukkitItemStack.getMethod("asNMSCopy", ItemStack.class);
GET_TAG = mcItemStack.getDeclaredMethod("getTagClone");
GET_TAG.setAccessible(true);
MODIFY_ITEM_STACK = craftMagicNumbers.getDeclaredMethod("modifyItemStack", ItemStack.class, String.class);
CRAFT_MAGIC_NUMBERS = craftMagicNumbers.getField("INSTANCE").get(null);
Class<?>[] parameters = new Class[]{String.class, int.class};
CONTAINS = Arrays.stream(nbtTagCompound.getDeclaredMethods()).filter(m ->
m.getReturnType() == boolean.class && Arrays.equals(m.getParameterTypes(), parameters)).findAny().orElseThrow();
} catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
// CREDIT: This function is based on the MiniNBT library: https://github.com/I-Al-Istannen/MiniNBT
public static String itemNBTToString(ItemStack item){
Object nmsItem;
try {
nmsItem = AS_NMS_COPY.invoke(null, item);
Object tag = getItemNBT(item);
if (tag == null) {
return null;
}
return tag.toString();
}
public static Object getItemNBT(ItemStack item){
try {
Object nmsItem = AS_NMS_COPY.invoke(null, item);
if (nmsItem == null) {
throw new NullPointerException("Unable to find a nms item clone for " + item);
}
return GET_TAG.invoke(nmsItem);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
Object tag = GET_TAG.invoke(nmsItem);
if (tag == null) {
return null;
}
return tag.toString();
public static boolean containsEntityTag(ItemStack itemStack){
Object tag = getItemNBT(itemStack);
if (tag == null) return false;
try {
return (Boolean) CONTAINS.invoke(tag, "EntityTag", 10/*CompoundTag*/);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}

Datei anzeigen

@ -4,15 +4,14 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.CommandBlock;
import org.bukkit.block.ShulkerBox;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;
@ -200,17 +199,26 @@ class Utils {
}
static String quote(String s) {
return "\"\\\"" + s + "\\\"\"";
return "\"\\\"" +
s.replace("\\", "\\\\\\\\").replace("\"", "\\\\\\\"") // escape " and \
+ "\\\"\"";
}
static ItemStack createArmorStandItem(ArmorStand as) {
EntityEquipment equipment = as.getEquipment();
if(equipment != null){
for(EquipmentSlot slot : EquipmentSlot.values()){
if(canArmorStandItemContain(equipment.getItem(slot))) return null;
}
}
ItemStack armorStand = new ItemStack(Material.ARMOR_STAND);
ItemStackReflections.setItemNBTFromString(armorStand, "{HideFlags:1,EntityTag:" + createEntityTag(as) + "}");
ItemStackReflections.setItemNBTFromString(armorStand, "{EntityTag:" + createEntityTag(as) + "}");
ItemMeta meta = armorStand.getItemMeta();
if(meta != null){
meta.setLore(createItemLore(as));
meta.setDisplayName(Config.configuredArmorStand);
meta.addEnchant(Enchantment.DURABILITY, 1, true);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
}
armorStand.setItemMeta(meta);
return armorStand;
@ -386,4 +394,13 @@ class Utils {
clone.setMetadata("clone", new FixedMetadataValue(AST.plugin, true));
return clone;
}
static boolean isConfiguredArmorStandItem(ItemStack item){
return item.getType() == Material.ARMOR_STAND && ItemStackReflections.containsEntityTag(item);
}
static boolean canArmorStandItemContain(ItemStack item){
return !isConfiguredArmorStandItem (item) &&
!(item.getItemMeta() instanceof BlockStateMeta meta && meta.getBlockState() instanceof ShulkerBox);
}
}

Datei anzeigen

@ -58,7 +58,7 @@ wgNoPerm: 'No permission to alter an armor stand in this region'
noCommandPerm: 'You do not have permission to use this command'
armorStand: 'Armor Stand'
none: 'None'
guiInUse: 'This armor stands GUI is in use'
guiInUse: "This armor stand's GUI is in use"
# New since v2.4.0:
noASNearBy: 'No armor stand found near by'
closestAS: 'Closest armor stand'
@ -115,7 +115,8 @@ enterNameC: 'To set armor stand name'
enterNameC2: 'Or to remove the name'
enterSkullC: 'To set MC username for skull'
#New since v4.4.4
invFullForArmorStandItem: 'Your inventory is full! The item was dropped at the armor stand.'
invFullForArmorStandItem: 'Your inventory is full! The item was dropped at the armor stand'
armorStandItemFail: "Armor stand can't be turned into an item because it contains disallowed items"
#############################
# Tool names & descriptions #
#############################