diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/config/Settings.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/config/Settings.java index e2ec6a8e..c47a986b 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/config/Settings.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/config/Settings.java @@ -1,14 +1,17 @@ package com.hibiscusmc.hmccosmetics.config; import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin; +import com.hibiscusmc.hmccosmetics.util.HMCCInventoryUtils; import com.hibiscusmc.hmccosmetics.util.MessagesUtil; import lombok.Getter; import lombok.Setter; import me.lojosho.shaded.configurate.ConfigurationNode; +import org.apache.commons.lang3.EnumUtils; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.util.Vector; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.logging.Level; @@ -48,11 +51,6 @@ public class Settings { private static final String COSMETIC_DISABLED_WORLDS_PATH = "disabled-worlds"; private static final String COSMETIC_PACKET_ENTITY_TELEPORT_COOLDOWN_PATH = "entity-cooldown-teleport-packet"; private static final String COSMETIC_BACKPACK_FORCE_RIDING_PACKET_PATH = "backpack-force-riding-packet"; - private static final String COSMETIC_FORCE_OFFHAND_COSMETIC_SHOW_PATH = "offhand-always-show"; - private static final String COSMETIC_ADD_ENCHANTS_HELMET_PATH = "helmet-add-enchantments"; - private static final String COSMETIC_ADD_ENCHANTS_CHESTPLATE_PATH = "chest-add-enchantments"; - private static final String COSMETIC_ADD_ENCHANTS_LEGGINGS_PATH = "leggings-add-enchantments"; - private static final String COSMETIC_ADD_ENCHANTS_BOOTS_PATH = "boots-add-enchantments"; private static final String COSMETIC_DESTROY_LOOSE_COSMETIC_PATH = "destroy-loose-cosmetics"; private static final String COSMETIC_BALLOON_HEAD_FORWARD_PATH = "balloon-head-forward"; private static final String MENU_SETTINGS_PATH = "menu-settings"; @@ -69,6 +67,7 @@ public class Settings { private static final String EQUIPABLE_COSMETIC_COLOR_PATH = "equipable-cosmetic-color"; private static final String LOCKED_COSMETIC_COLOR_PATH = "locked-cosmetic-color"; private static final String ENABLED_PATH = "enabled"; + private static final String SLOT_OPTIONS_PATH = "slot-options"; @Getter private static String defaultMenu; @@ -94,14 +93,7 @@ public class Settings { private static boolean worldGuardMoveCheck; @Getter private static boolean cosmeticEmoteBlockCheck; - @Getter - private static boolean addHelmetEnchants; - @Getter - private static boolean addChestplateEnchants; - @Getter - private static boolean addLeggingEnchants; - @Getter - private static boolean addBootsEnchants; + private static final HashMap slotOptions = new HashMap<>(); @Getter private static boolean emoteAirCheck; @Getter @@ -113,8 +105,6 @@ public class Settings { @Getter private static boolean backpackForceRidingEnabled; @Getter - private static boolean cosmeticForceOffhandCosmeticShow; - @Getter private static boolean emotesEnabled; @Getter private static boolean disabledGamemodesEnabled; @@ -202,16 +192,23 @@ public class Settings { emoteInvincible = cosmeticSettings.node(COSMETIC_EMOTE_INVINCIBLE_PATH).getBoolean(false); destroyLooseCosmetics = cosmeticSettings.node(COSMETIC_DESTROY_LOOSE_COSMETIC_PATH).getBoolean(false); backpackForceRidingEnabled = cosmeticSettings.node(COSMETIC_BACKPACK_FORCE_RIDING_PACKET_PATH).getBoolean(false); - addHelmetEnchants = cosmeticSettings.node(COSMETIC_ADD_ENCHANTS_HELMET_PATH).getBoolean(false); - addChestplateEnchants = cosmeticSettings.node(COSMETIC_ADD_ENCHANTS_CHESTPLATE_PATH).getBoolean(false); - addLeggingEnchants = cosmeticSettings.node(COSMETIC_ADD_ENCHANTS_LEGGINGS_PATH).getBoolean(false); - addBootsEnchants = cosmeticSettings.node(COSMETIC_ADD_ENCHANTS_BOOTS_PATH).getBoolean(false); + + cosmeticSettings.node(SLOT_OPTIONS_PATH).childrenMap().forEach((key, value) -> { + EquipmentSlot slot = convertConfigToEquipment(key.toString().toLowerCase()); + if (slot == null) { + MessagesUtil.sendDebugMessages("Invalid slot option: " + key, Level.WARNING); + return; + } + boolean addEnchantments = value.node("add-enchantments").getBoolean(false); + boolean requireEmpty = value.node("require-empty").getBoolean(false); + slotOptions.put(slot, new SlotOptionConfig(slot, addEnchantments, requireEmpty)); + }); + tickPeriod = cosmeticSettings.node(TICK_PERIOD_PATH).getInt(-1); viewDistance = cosmeticSettings.node(VIEW_DISTANCE_PATH).getInt(-3); emoteCameraEnabled = cosmeticSettings.node(COSMETIC_EMOTE_CAMERA_PATH).getBoolean(true); emoteMoveCheck = cosmeticSettings.node(COSMETIC_EMOTE_MOVE_CHECK_PATH).getBoolean(false); packetEntityTeleportCooldown = cosmeticSettings.node(COSMETIC_PACKET_ENTITY_TELEPORT_COOLDOWN_PATH).getInt(-1); - cosmeticForceOffhandCosmeticShow = cosmeticSettings.node(COSMETIC_FORCE_OFFHAND_COSMETIC_SHOW_PATH).getBoolean(false); balloonHeadForward = cosmeticSettings.node(COSMETIC_BALLOON_HEAD_FORWARD_PATH).getBoolean(false); ConfigurationNode menuSettings = source.node(MENU_SETTINGS_PATH); @@ -257,24 +254,9 @@ public class Settings { return new Vector(config.node("x").getDouble(), config.node("y").getDouble(), config.node("z").getDouble()); } - public static boolean getShouldAddEnchants(EquipmentSlot slot) { - switch (slot) { - case HEAD -> { - return addHelmetEnchants; - } - case CHEST -> { - return addChestplateEnchants; - } - case LEGS -> { - return addLeggingEnchants; - } - case FEET -> { - return addBootsEnchants; - } - default -> { - return false; - } - } + public static SlotOptionConfig getSlotOption(EquipmentSlot slot) { + if (!slotOptions.containsKey(slot)) slotOptions.put(slot, new SlotOptionConfig(slot, false, false)); + return slotOptions.get(slot); } public static void setDebugMode(boolean newSetting) { @@ -286,4 +268,16 @@ public class Settings { plugin.saveConfig(); } + + private static EquipmentSlot convertConfigToEquipment(String slot) { + return switch (slot) { + case "helmet" -> EquipmentSlot.HEAD; + case "chestplate" -> EquipmentSlot.CHEST; + case "leggings" -> EquipmentSlot.LEGS; + case "boots" -> EquipmentSlot.FEET; + case "offhand" -> EquipmentSlot.OFF_HAND; + case "mainhand" -> EquipmentSlot.HAND; + default -> null; + }; + } } diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/config/SlotOptionConfig.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/config/SlotOptionConfig.java new file mode 100644 index 00000000..9f5038ad --- /dev/null +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/config/SlotOptionConfig.java @@ -0,0 +1,20 @@ +package com.hibiscusmc.hmccosmetics.config; + +import lombok.Getter; +import org.bukkit.inventory.EquipmentSlot; + +public class SlotOptionConfig { + + @Getter + private final EquipmentSlot slot; + @Getter + private final boolean addEnchantments; + @Getter + private final boolean requireEmpty; + + public SlotOptionConfig(EquipmentSlot slot, boolean addEnchantments, boolean requireEmpty) { + this.slot = slot; + this.addEnchantments = addEnchantments; + this.requireEmpty = requireEmpty; + } +} diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/types/CosmeticArmorType.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/types/CosmeticArmorType.java index 92289d90..79df281d 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/types/CosmeticArmorType.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/types/CosmeticArmorType.java @@ -30,9 +30,9 @@ public class CosmeticArmorType extends Cosmetic { if (user.getUserEmoteManager().isPlayingEmote() || user.isInWardrobe()) return; Entity entity = Bukkit.getEntity(user.getUniqueId()); if (entity == null) return; - if (!Settings.isCosmeticForceOffhandCosmeticShow() - && equipSlot.equals(EquipmentSlot.OFF_HAND) - && ((user.getEntity() instanceof Player) && !user.getPlayer().getInventory().getItemInOffHand().getType().isAir())) return; + if (Settings.getSlotOption(equipSlot).isRequireEmpty() && entity instanceof HumanEntity humanEntity) { + if (!humanEntity.getInventory().getItem(equipSlot).getType().isAir()) return; + } ItemStack item = getItem(user); if (item == null) return; PacketManager.equipmentSlotUpdate(entity.getEntityId(), equipSlot, item, HMCCPacketManager.getViewers(entity.getLocation())); @@ -44,7 +44,7 @@ public class CosmeticArmorType extends Cosmetic { public ItemStack getItem(@NotNull CosmeticUser user, ItemStack cosmeticItem) { if (!(user.getEntity() instanceof HumanEntity humanEntity)) return null; - if (Settings.getShouldAddEnchants(equipSlot)) { + if (Settings.getSlotOption(equipSlot).isAddEnchantments()) { ItemStack equippedItem = humanEntity.getInventory().getItem(equipSlot); cosmeticItem.addUnsafeEnchantments(equippedItem.getEnchantments()); } diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/listener/PlayerGameListener.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/listener/PlayerGameListener.java index 0dd510a6..f7f4d875 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/listener/PlayerGameListener.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/listener/PlayerGameListener.java @@ -327,9 +327,11 @@ public class PlayerGameListener implements Listener { event.getPlayer().getInventory().setItem(event.getPreviousSlot(), event.getPlayer().getInventory().getItem(event.getPreviousSlot())); //NMSHandlers.getHandler().slotUpdate(event.getPlayer(), event.getPreviousSlot()); - Bukkit.getScheduler().runTaskLater(HMCCosmeticsPlugin.getInstance(), () -> { - user.updateCosmetic(CosmeticSlot.MAINHAND); - }, 2); + if (user.hasCosmeticInSlot(CosmeticSlot.MAINHAND)) { + Bukkit.getScheduler().runTaskLater(HMCCosmeticsPlugin.getInstance(), () -> { + user.updateCosmetic(CosmeticSlot.MAINHAND); + }, 2); + } // #84, Riptides mess with backpacks ItemStack currentItem = event.getPlayer().getInventory().getItem(event.getNewSlot()); @@ -486,6 +488,10 @@ public class PlayerGameListener implements Listener { if (!user.isInWardrobe()) { for (Cosmetic cosmetic : user.getCosmetics()) { if ((cosmetic instanceof CosmeticArmorType cosmeticArmorType)) { + boolean requireEmpty = Settings.getSlotOption(cosmeticArmorType.getEquipSlot()).isRequireEmpty(); + boolean isAir = user.getPlayer().getInventory().getItem(cosmeticArmorType.getEquipSlot()).getType().isAir(); + MessagesUtil.sendDebugMessages("Menu Fired (Checks) - " + cosmeticArmorType.getId() + " - " + requireEmpty + " - " + isAir); + if (requireEmpty && !isAir) continue; items.put(HMCCInventoryUtils.getPacketArmorSlot(cosmeticArmorType.getEquipSlot()), user.getUserCosmeticItem(cosmeticArmorType)); } } @@ -536,9 +542,14 @@ public class PlayerGameListener implements Listener { int slot = event.getPacket().getIntegers().read(2); MessagesUtil.sendDebugMessages("SetSlot Slot " + slot); - if (slot == 45 && user.hasCosmeticInSlot(CosmeticSlot.OFFHAND) && player.getInventory().getItemInOffHand().getType().isAir()) { - event.getPacket().getItemModifier().write(0, user.getUserCosmeticItem(CosmeticSlot.OFFHAND)); + CosmeticSlot cosmeticSlot = HMCCInventoryUtils.NMSCosmeticSlot(slot); + EquipmentSlot equipmentSlot = HMCCInventoryUtils.getPacketArmorSlot(slot); + if (cosmeticSlot == null || equipmentSlot == null) return; + if (!user.hasCosmeticInSlot(cosmeticSlot)) return; + if (Settings.getSlotOption(equipmentSlot).isRequireEmpty()) { + if (!player.getInventory().getItem(equipmentSlot).getType().isAir()) return; } + event.getPacket().getItemModifier().write(0, user.getUserCosmeticItem(cosmeticSlot)); } }); } @@ -563,20 +574,17 @@ public class PlayerGameListener implements Listener { if (user.getPlayer() == event.getPlayer()) continue; // When a player scrolls real fast, it messes up the mainhand. This fixes it armor.set(i, new Pair<>(pair.getFirst(), user.getPlayer().getInventory().getItemInMainHand())); } - case OFFHAND -> { - if (Settings.isCosmeticForceOffhandCosmeticShow() && user.hasCosmeticInSlot(CosmeticSlot.OFFHAND)) { - ItemStack item = user.getUserCosmeticItem(CosmeticSlot.OFFHAND); - if (item == null) continue; - Pair offhandPair = new Pair<>(EnumWrappers.ItemSlot.OFFHAND, item); - armor.set(i, offhandPair); - } - } default -> { - CosmeticArmorType cosmeticArmor = (CosmeticArmorType) user.getCosmetic(HMCCInventoryUtils.getItemSlotToCosmeticSlot(pair.getFirst())); + EquipmentSlot slot = HMCCInventoryUtils.getEquipmentSlot(pair.getFirst()); + CosmeticSlot cosmeticSlot = HMCCInventoryUtils.getItemSlotToCosmeticSlot(pair.getFirst()); + if (slot == null || cosmeticSlot == null) continue; + if (Settings.getSlotOption(slot).isRequireEmpty() + && !user.getPlayer().getInventory().getItem(slot).getType().isAir()) continue; + CosmeticArmorType cosmeticArmor = (CosmeticArmorType) user.getCosmetic(cosmeticSlot); if (cosmeticArmor == null) continue; ItemStack item = user.getUserCosmeticItem(cosmeticArmor); if (item == null) continue; - Pair armorPair = new Pair<>(HMCCInventoryUtils.itemBukkitSlot(cosmeticArmor.getEquipSlot()), item); + Pair armorPair = new Pair<>(HMCCInventoryUtils.itemBukkitSlot(slot), item); armor.set(i, armorPair); } } diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUser.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUser.java index 2fb853b2..0069ce83 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUser.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUser.java @@ -29,6 +29,7 @@ import org.bukkit.Bukkit; import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.entity.Entity; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -195,13 +196,17 @@ public class CosmeticUser { for (Cosmetic cosmetic : getCosmetics()) { if (cosmetic instanceof CosmeticArmorType armorType) { if (getUserEmoteManager().isPlayingEmote() || isInWardrobe()) return; - if (!Settings.isCosmeticForceOffhandCosmeticShow() - && armorType.getEquipSlot().equals(EquipmentSlot.OFF_HAND) - && !getPlayer().getInventory().getItemInOffHand().getType().isAir()) continue; + if (!(getEntity() instanceof HumanEntity humanEntity)) return; + + boolean requireEmpty = Settings.getSlotOption(armorType.getEquipSlot()).isRequireEmpty(); + boolean isAir = humanEntity.getInventory().getItem(armorType.getEquipSlot()).getType().isAir(); + MessagesUtil.sendDebugMessages("updateCosmetic (All) - " + armorType.getId() + " - " + requireEmpty + " - " + isAir); + if (requireEmpty && !isAir) continue; + items.put(HMCCInventoryUtils.getEquipmentSlot(armorType.getSlot()), armorType.getItem(this)); - continue; + } else { + updateCosmetic(cosmetic.getSlot()); } - updateCosmetic(cosmetic.getSlot()); } if (items.isEmpty() || getEntity() == null) return; PacketManager.equipmentSlotUpdate(getEntity().getEntityId(), items, HMCCPlayerUtils.getNearbyPlayers(getEntity().getLocation())); diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/util/HMCCInventoryUtils.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/util/HMCCInventoryUtils.java index 271649bf..54b3d771 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/util/HMCCInventoryUtils.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/util/HMCCInventoryUtils.java @@ -153,6 +153,32 @@ public class HMCCInventoryUtils { } } + public static EquipmentSlot getEquipmentSlot(@NotNull EnumWrappers.ItemSlot slot) { + switch (slot) { + case HEAD -> { + return EquipmentSlot.HEAD; + } + case CHEST -> { + return EquipmentSlot.CHEST; + } + case LEGS -> { + return EquipmentSlot.LEGS; + } + case FEET -> { + return EquipmentSlot.FEET; + } + case OFFHAND -> { + return EquipmentSlot.OFF_HAND; + } + case MAINHAND -> { + return EquipmentSlot.HAND; + } + default -> { + return null; + } + } + } + public static boolean isCosmeticItem(ItemStack itemStack) { if (itemStack == null) return false; itemStack = itemStack.clone(); diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index 19e4fc62..07b7576f 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -51,13 +51,26 @@ cosmetic-settings: # This is useful for servers with a lot of backpacks, as they are passengers, which means the client will update their position automatically within an area where the entity is located. entity-cooldown-teleport-packet: 500 # This forces the offhand to always show the cosmetic. False means it will only show when the slot is empty. - # There is a small visual blip for a tick when you put a new item in the offhand slot, hence why its disabled by default. - offhand-always-show: false - helmet-add-enchantments: false # If the plugin should keep enchants on helmets. This is useful as some enchantments are client side only. - chest-add-enchantments: false # If the plugin should keep enchants on chestplate. This is useful as some enchantments are client side only. - leggings-add-enchantments: false # If the plugin should keep enchants on leggings. This is useful as some enchantments are client side only. - boots-add-enchantments: false # If the plugin should keep enchants on boots. This is useful as some enchantments are client side only. + slot-options: + helmet: + # If the plugin should keep enchants on helmets. This is useful as some enchantments are client side only. + add-enchantments: false + # Should the player slot be empty for the cosmetic to be applied? A cosmetic will be "hidden" if a player has an item equipped in the slot + require-empty: true + chestplate: + add-enchantments: false + require-empty: false + leggings: + add-enchantments: false + require-empty: false + boots: + add-enchantments: false + require-empty: false + offhand: + add-enchantments: false + # There is a small visual blip for a tick when you put a new item in the offhand slot, hence why its enabled by default. + require-empty: true # This attempts to destroy cosmetics that get loose in the wild, such as through a player entering creative mode. # Most servers who don't use creative mode and have properly set up the plugin should have no need for this and can leave it disabled.