diff --git a/.idea/modules/HMCCosmetics.main.iml b/.idea/modules/HMCCosmetics.main.iml index 97829813..85578b10 100644 --- a/.idea/modules/HMCCosmetics.main.iml +++ b/.idea/modules/HMCCosmetics.main.iml @@ -4,7 +4,6 @@ - PAPER MCP ADVENTURE diff --git a/build.gradle b/build.gradle index e4a23b5e..5aa6906b 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,8 @@ repositories { maven { url = 'https://papermc.io/repo/repository/maven-public/' } maven { url = 'https://repo.mattstudios.me/artifactory/public/' } maven { url = 'https://jitpack.io' } - maven { url = 'https://repo.leonardobishop.com/releases/' } maven { url = 'https://repo.dmulloy2.net/repository/public/' } + maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } } dependencies { @@ -23,6 +23,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:22.0.0' compileOnly 'com.comphenix.protocol:ProtocolLib:4.7.0' compileOnly 'net.kyori:adventure-api:4.9.3' + compileOnly 'me.clip:placeholderapi:2.11.1' implementation 'net.kyori:adventure-text-minimessage:4.2.0-SNAPSHOT' implementation 'net.kyori:adventure-platform-bukkit:4.0.0' implementation 'dev.triumphteam:triumph-gui:3.0.3' diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java b/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java index 98c273c6..a78ec4f9 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java @@ -8,6 +8,7 @@ import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager; import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu; import io.github.fisher2911.hmccosmetics.listener.ClickListener; +import io.github.fisher2911.hmccosmetics.listener.HatRemoveFixListener; import io.github.fisher2911.hmccosmetics.listener.JoinListener; import io.github.fisher2911.hmccosmetics.listener.RespawnListener; import io.github.fisher2911.hmccosmetics.listener.TeleportListener; @@ -16,6 +17,7 @@ import io.github.fisher2911.hmccosmetics.message.Messages; import io.github.fisher2911.hmccosmetics.user.UserManager; import me.mattstudios.mf.base.CommandManager; import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import java.util.Arrays; @@ -31,6 +33,7 @@ public class HMCCosmetics extends JavaPlugin { private MessageHandler messageHandler; private CosmeticsMenu cosmeticsMenu; private CommandManager commandManager; + private boolean papiEnabled; @Override public void onEnable() { @@ -48,6 +51,8 @@ public class HMCCosmetics extends JavaPlugin { this.registerListeners(); this.userManager.startTeleportTask(); + + this.papiEnabled = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; } @Override @@ -61,7 +66,8 @@ public class HMCCosmetics extends JavaPlugin { List.of(new JoinListener(this), new ClickListener(this), new TeleportListener(this), - new RespawnListener(this)). + new RespawnListener(this), + new HatRemoveFixListener(this)). forEach(listener -> this.getServer().getPluginManager().registerEvents(listener, this) ); @@ -109,5 +115,9 @@ public class HMCCosmetics extends JavaPlugin { public ProtocolManager getProtocolManager() { return protocolManager; } + + public boolean isPapiEnabled() { + return papiEnabled; + } } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java b/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java index 527beb62..d0f7e7e2 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java @@ -3,8 +3,6 @@ package io.github.fisher2911.hmccosmetics.command; import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu; -import io.github.fisher2911.hmccosmetics.gui.DyeSelectorGui; -import io.github.fisher2911.hmccosmetics.message.Message; import io.github.fisher2911.hmccosmetics.message.MessageHandler; import io.github.fisher2911.hmccosmetics.message.Messages; import io.github.fisher2911.hmccosmetics.message.Placeholder; @@ -17,12 +15,9 @@ import me.mattstudios.mf.annotations.Permission; import me.mattstudios.mf.annotations.SubCommand; import me.mattstudios.mf.base.CommandBase; import org.bukkit.Bukkit; -import org.bukkit.Color; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import java.util.Map; -import java.util.Objects; import java.util.Optional; @Command("cosmetics") diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/config/DyeGuiSerializer.java b/src/main/java/io/github/fisher2911/hmccosmetics/config/DyeGuiSerializer.java index 48855314..f45ee1f7 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/config/DyeGuiSerializer.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/config/DyeGuiSerializer.java @@ -5,6 +5,7 @@ import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.gui.ColorItem; import io.github.fisher2911.hmccosmetics.gui.DyeSelectorGui; import io.github.fisher2911.hmccosmetics.message.Adventure; +import io.github.fisher2911.hmccosmetics.util.StringUtils; import org.bukkit.Color; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.configurate.ConfigurationNode; @@ -81,10 +82,14 @@ public class DyeGuiSerializer implements TypeSerializer { guiItemMap.put(slot, new ColorItem(guiItem.getItemStack(), Color.fromRGB(red, green, blue))); } + String title = titleNode.getString(); + + if (title == null) title = ""; + return new DyeSelectorGui( plugin, Adventure.SERIALIZER.serialize( - Adventure.MINI_MESSAGE.parse(titleNode.getString())), + Adventure.MINI_MESSAGE.parse(title)), rowsNode.getInt(), guiItemMap); } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/config/GuiSerializer.java b/src/main/java/io/github/fisher2911/hmccosmetics/config/GuiSerializer.java index 0b170955..408f93fb 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/config/GuiSerializer.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/config/GuiSerializer.java @@ -4,6 +4,8 @@ import dev.triumphteam.gui.guis.GuiItem; import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.gui.CosmeticGui; import io.github.fisher2911.hmccosmetics.message.Adventure; +import io.github.fisher2911.hmccosmetics.papi.PAPIHook; +import io.github.fisher2911.hmccosmetics.util.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.serialize.SerializationException; @@ -43,7 +45,7 @@ public class GuiSerializer implements TypeSerializer { final ConfigurationNode rowsNode = this.nonVirtualNode(source, ROWS); final ConfigurationNode itemsNode = source.node(ITEMS); - final var childrenMap = source.node(ITEMS).childrenMap(); + final var childrenMap = itemsNode.childrenMap(); final Map guiItemMap = new HashMap<>(); @@ -60,9 +62,13 @@ public class GuiSerializer implements TypeSerializer { guiItemMap.put(slot, guiItem); } + String title = titleNode.getString(); + + if (title == null) title = ""; + return new CosmeticGui(plugin, Adventure.SERIALIZER.serialize( - Adventure.MINI_MESSAGE.parse(titleNode.getString())), + Adventure.MINI_MESSAGE.parse(title)), rowsNode.getInt(), guiItemMap); } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/config/ItemSerializer.java b/src/main/java/io/github/fisher2911/hmccosmetics/config/ItemSerializer.java index 42ccdac0..85f1530c 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/config/ItemSerializer.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/config/ItemSerializer.java @@ -100,9 +100,6 @@ public class ItemSerializer implements TypeSerializer { Material.class, Material.AIR); final int amount = amountNode.getInt(); final Component name = StringUtils.parse(nameNode.getString()); -// Adventure.MINI_MESSAGE.parse( -// Utils.replaceIfNull(nameNode.getString(), "") -// ); final boolean unbreakable = unbreakableNode.getBoolean(); final boolean glowing = glowingNode.getBoolean(); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticGui.java b/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticGui.java index ef6e2ce7..2c69b743 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticGui.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticGui.java @@ -10,11 +10,13 @@ import io.github.fisher2911.hmccosmetics.message.MessageHandler; import io.github.fisher2911.hmccosmetics.message.Messages; import io.github.fisher2911.hmccosmetics.message.Placeholder; import io.github.fisher2911.hmccosmetics.user.User; +import io.github.fisher2911.hmccosmetics.util.StringUtils; import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder; import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; import java.util.HashMap; import java.util.Locale; @@ -27,6 +29,7 @@ public class CosmeticGui { protected final MessageHandler messageHandler; protected final String title; protected final int rows; + protected final Map itemStackMap; protected final Map guiItemMap; protected Gui gui; @@ -40,6 +43,8 @@ public class CosmeticGui { this.title = title; this.rows = rows; this.guiItemMap = guiItemMap; + this.itemStackMap = new HashMap<>(); + this.guiItemMap.forEach((key, value) -> itemStackMap.put(key, value.getItemStack())); } private void setItems(final User user) { @@ -55,6 +60,14 @@ public class CosmeticGui { final GuiItem guiItem = entry.getValue(); + final ItemStack itemStack = this.itemStackMap.get(slot); + + if (itemStack == null) continue; + + guiItem.setItemStack( + ItemBuilder.from(itemStack.clone()).papiPlaceholders(player).build() + ); + if (guiItem instanceof final ArmorItem armorItem) { final Map placeholders = new HashMap<>(); @@ -91,6 +104,7 @@ public class CosmeticGui { armorItem.getItemStack(hasPermission) ).namePlaceholders(placeholders). lorePlaceholders(placeholders). + papiPlaceholders(player). build(), event -> { if (!hasPermission) { @@ -152,7 +166,7 @@ public class CosmeticGui { final User user = optionalUser.get(); this.gui = Gui.gui(). - title(Adventure.MINI_MESSAGE.parse(this.title)). + title(Adventure.MINI_MESSAGE.parse(StringUtils.applyPapiPlaceholders(user.getPlayer(), this.title))). rows(this.rows). create(); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticsMenu.java b/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticsMenu.java index 648326ea..11cfa72f 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticsMenu.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticsMenu.java @@ -63,8 +63,6 @@ public class CosmeticsMenu { if (gui instanceof final DyeSelectorGui dyeSelectorGui) { dyeSelectorGui.getGui(user, armorItem).open(player); - } else { - player.sendMessage(gui.getClass().toString()); } } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/gui/DyeSelectorGui.java b/src/main/java/io/github/fisher2911/hmccosmetics/gui/DyeSelectorGui.java index 6d3ef248..c116d8b6 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/gui/DyeSelectorGui.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/gui/DyeSelectorGui.java @@ -1,23 +1,25 @@ package io.github.fisher2911.hmccosmetics.gui; -import dev.triumphteam.gui.builder.item.ItemBuilder; import dev.triumphteam.gui.guis.Gui; import dev.triumphteam.gui.guis.GuiItem; import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; import io.github.fisher2911.hmccosmetics.user.User; +import io.github.fisher2911.hmccosmetics.util.StringUtils; import io.github.fisher2911.hmccosmetics.util.builder.ColorBuilder; +import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder; import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; import org.bukkit.Color; -import org.bukkit.Material; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.LeatherArmorMeta; +import java.util.HashMap; import java.util.Map; import java.util.Optional; -public class DyeSelectorGui extends CosmeticGui{ +public class DyeSelectorGui extends CosmeticGui { public DyeSelectorGui( final HMCCosmetics plugin, @@ -29,12 +31,25 @@ public class DyeSelectorGui extends CosmeticGui{ public Gui getGui(final User user, final ArmorItem armorItem) { final Gui gui = Gui.gui(). - title(Component.text(this.title)). + title(Component.text(StringUtils.applyPapiPlaceholders(user.getPlayer(), this.title))). rows(rows). create(); + final Player player = user.getPlayer(); + for (final var entry : this.guiItemMap.entrySet()) { - gui.setItem(entry.getKey(), entry.getValue()); + + final GuiItem guiItem = entry.getValue(); + + final ItemStack itemStack = this.itemStackMap.get(entry.getKey()); + + if (itemStack == null) continue; + + guiItem.setItemStack( + ItemBuilder.from(itemStack.clone()).papiPlaceholders(player).build() + ); + + gui.setItem(entry.getKey(), guiItem); } gui.setDefaultClickAction(event -> { diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java b/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java index 84f64028..4969598e 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java @@ -10,6 +10,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.PlayerInventory; +import org.spigotmc.event.entity.EntityMountEvent; import java.util.Optional; diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/listener/HatRemoveFixListener.java b/src/main/java/io/github/fisher2911/hmccosmetics/listener/HatRemoveFixListener.java new file mode 100644 index 00000000..328cbf49 --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/listener/HatRemoveFixListener.java @@ -0,0 +1,47 @@ +package io.github.fisher2911.hmccosmetics.listener; + +import io.github.fisher2911.hmccosmetics.HMCCosmetics; +import io.github.fisher2911.hmccosmetics.user.UserManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.spigotmc.event.entity.EntityMountEvent; + +public class HatRemoveFixListener implements Listener { + + private final HMCCosmetics plugin; + private final UserManager userManager; + + public HatRemoveFixListener(final HMCCosmetics plugin) { + this.plugin = plugin; + this.userManager = this.plugin.getUserManager(); + } + + @EventHandler + public void onInventoryClick(final InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof final Player player)) return; + this.fixHat(player); + } + + @EventHandler + public void onInventoryDrag(final InventoryDragEvent event) { + if (!(event.getWhoClicked() instanceof final Player player)) return; + this.fixHat(player); + } + + @EventHandler + public void test(final EntityMountEvent event) { + if (!(event.getEntity() instanceof final Player player)) return; + this.fixHat(player); + } + + private void fixHat(final Player player) { + Bukkit.getScheduler().runTaskLater( + this.plugin, + () -> this.userManager.get(player.getUniqueId()).ifPresent(this.userManager::setFakeHelmet), + 1); + } +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/message/MessageHandler.java b/src/main/java/io/github/fisher2911/hmccosmetics/message/MessageHandler.java index 7ae9447d..1a4c8d41 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/message/MessageHandler.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/message/MessageHandler.java @@ -1,6 +1,7 @@ package io.github.fisher2911.hmccosmetics.message; import io.github.fisher2911.hmccosmetics.HMCCosmetics; +import io.github.fisher2911.hmccosmetics.papi.PAPIHook; import io.github.fisher2911.hmccosmetics.util.StringUtils; import io.github.fisher2911.hmccosmetics.util.Utils; import net.kyori.adventure.platform.bukkit.BukkitAudiences; @@ -40,22 +41,23 @@ public class MessageHandler { } /** - * - * @param sender receiver of message - * @param key message key + * @param sender receiver of message + * @param key message key * @param placeholders placeholders */ public void sendMessage(final CommandSender sender, final Message key, final Map placeholders) { - final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders); - final Component component = Adventure.MINI_MESSAGE.parse(message); - this.adventure.sender(sender).sendMessage(component); + final String message = this.getPapiPlaceholders( + sender, + StringUtils.applyPlaceholders(this.getMessage(key), placeholders) + ); + final Component component = Adventure.MINI_MESSAGE.parse(message); + this.adventure.sender(sender).sendMessage(component); } /** - * * @param sender receiver of message - * @param key message key + * @param key message key */ public void sendMessage(final CommandSender sender, final Message key) { @@ -63,22 +65,23 @@ public class MessageHandler { } /** - * - * @param player receiver of message - * @param key message key + * @param player receiver of message + * @param key message key * @param placeholders placeholders */ public void sendActionBar(final Player player, final Message key, final Map placeholders) { - final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders); + final String message = this.getPapiPlaceholders( + player, + StringUtils.applyPlaceholders(this.getMessage(key), placeholders) + ); Component component = Adventure.MINI_MESSAGE.parse(message); this.adventure.player(player).sendActionBar(component); } /** - * * @param player receiver of message - * @param key message key + * @param key message key */ public void sendActionBar(final Player player, final Message key) { @@ -86,22 +89,23 @@ public class MessageHandler { } /** - * - * @param player receiver of message - * @param key message key + * @param player receiver of message + * @param key message key * @param placeholders placeholders */ public void sendTitle(final Player player, final Message key, final Map placeholders) { - final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders); + final String message = this.getPapiPlaceholders( + player, + StringUtils.applyPlaceholders(this.getMessage(key), placeholders) + ); Component component = Adventure.MINI_MESSAGE.parse(message); this.adventure.player(player).showTitle(Title.title(component, Component.empty())); } /** - * * @param player receiver of message - * @param key message key + * @param key message key */ public void sendTitle(final Player player, final Message key) { @@ -109,7 +113,6 @@ public class MessageHandler { } /** - * * @param key message key * @return message, or empty string if message not found */ @@ -153,4 +156,12 @@ public class MessageHandler { this.messageMap.put(key, new Message(key, message, messageType)); } } + + private String getPapiPlaceholders(final CommandSender sender, final String message) { + if (sender instanceof final Player player) { + return StringUtils.applyPapiPlaceholders(player, message); + } + return StringUtils.applyPapiPlaceholders(null, message); + } + } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/message/Placeholder.java b/src/main/java/io/github/fisher2911/hmccosmetics/message/Placeholder.java index 3ab1523c..0eb63af2 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/message/Placeholder.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/message/Placeholder.java @@ -10,4 +10,5 @@ public class Placeholder { public static final String PLAYER = "%player%"; public static final String ENABLED = "%enabled%"; public static final String ALLOWED = "%allowed%"; + } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/papi/PAPIHook.java b/src/main/java/io/github/fisher2911/hmccosmetics/papi/PAPIHook.java new file mode 100644 index 00000000..c1e855fa --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/papi/PAPIHook.java @@ -0,0 +1,12 @@ +package io.github.fisher2911.hmccosmetics.papi; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.entity.Player; + +public class PAPIHook { + + public static String parse(final Player player, final String string) { + return PlaceholderAPI.setPlaceholders(player, string); + } + +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java b/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java index 0553910f..0ec8ff89 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java @@ -1,32 +1,46 @@ package io.github.fisher2911.hmccosmetics.user; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.wrappers.EnumWrappers; +import com.comphenix.protocol.wrappers.Pair; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; import io.github.fisher2911.hmccosmetics.message.MessageHandler; import io.github.fisher2911.hmccosmetics.message.Messages; -import io.github.fisher2911.hmccosmetics.util.Keys; +import io.github.fisher2911.hmccosmetics.message.Placeholder; +import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder; +import it.unimi.dsi.fastutil.ints.IntArrayList; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.UUID; public class User { private final UUID uuid; private final PlayerArmor playerArmor; - private ArmorStand attached; private ArmorItem lastSetItem; - public User(final UUID uuid, final PlayerArmor playerArmor) { + private boolean hasArmorStand; + private final int armorStandId; + + public User(final UUID uuid, final PlayerArmor playerArmor, final int armorStandId) { this.uuid = uuid; this.playerArmor = playerArmor; + this.armorStandId = armorStandId; } public @Nullable Player getPlayer() { @@ -41,6 +55,19 @@ public class User { return playerArmor; } + public void setPlayerArmor(final PlayerArmor playerArmor) { + this.playerArmor.setBackpack(playerArmor.getBackpack()); + this.playerArmor.setHat(playerArmor.getHat()); + } + + public void removeAllCosmetics() { + this.setPlayerArmor(PlayerArmor.empty()); + } + + public int getArmorStandId() { + return armorStandId; + } + public void setBackpack(final ArmorItem backpack) { this.playerArmor.setBackpack(backpack); this.lastSetItem = backpack; @@ -129,83 +156,140 @@ public class User { return true; } - public void detach() { - if (this.attached != null) { - this.attached.remove(); + public void spawnArmorStand(final Player other) { + final Player player = this.getPlayer(); + + if (player == null) return; + + final Location location = player.getLocation(); + + final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + final PacketContainer packet = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); + + // Entity ID + packet.getIntegers().write(0, this.armorStandId); + // Entity Type +// packet.getIntegers().write(6, 78); + // Set yaw pitch + packet.getIntegers().write(4, (int) location.getPitch()); + packet.getIntegers().write(5, (int) location.getYaw()); + // Set location + packet.getDoubles().write(0, 0d); + packet.getDoubles().write(1, -5d); + packet.getDoubles().write(2, 0d); + // Set UUID + packet.getUUIDs().write(0, UUID.randomUUID()); + + packet.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND); + + final PacketContainer ridingPacket = new PacketContainer(PacketType.Play.Server.MOUNT); + ridingPacket. + getIntegers(). + write(0, player.getEntityId()); + ridingPacket.getIntegerArrays().write(0, new int[]{this.armorStandId}); + + try { + protocolManager.sendServerPacket(other, packet); + protocolManager.sendServerPacket(other, ridingPacket); + } catch (InvocationTargetException e) { + e.printStackTrace(); } } - // teleports armor stand to the correct position - public void updateArmorStand() { - final ArmorItem backpackArmorItem = this.playerArmor.getBackpack(); - if (backpackArmorItem == null) { - this.despawnAttached(); + public void spawnArmorStand() { + if (this.hasArmorStand) { + this.updateArmorStand(); return; } - final ItemStack backpackItem = backpackArmorItem.getItemStack(); + for (final Player p : Bukkit.getOnlinePlayers()) { + this.spawnArmorStand(p); + } - if (backpackItem == null || backpackItem.getType() == Material.AIR) { - this.despawnAttached(); + this.hasArmorStand = true; + } + + public void updateArmorStand() { + if (!this.hasArmorStand) { + this.spawnArmorStand(); return; } final Player player = this.getPlayer(); - if (player == null) { - this.despawnAttached(); - return; + if (player == null) return; + + final List> equipmentList = new ArrayList<>(); + + final Map placeholders = Map.of(Placeholder.ALLOWED, "true", + Placeholder.ENABLED, "true"); + + equipmentList.add(new Pair<>(EnumWrappers.ItemSlot.HEAD, + ItemBuilder.from(this.playerArmor.getBackpack().getItemStack()). + namePlaceholders(placeholders). + lorePlaceholders(placeholders). + build() + )); + + final PacketContainer armorPacket = new PacketContainer(PacketType.Play.Server.ENTITY_EQUIPMENT); + armorPacket.getIntegers().write(0, this.armorStandId); + armorPacket.getSlotStackPairLists().write(0, equipmentList); + + final Location location = player.getLocation(); + + final PacketContainer metaContainer = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); + + WrappedDataWatcher metaData = new WrappedDataWatcher(); + metaData.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, WrappedDataWatcher.Registry.get(Byte.class)), (byte) (0x20)); + metaData.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, WrappedDataWatcher.Registry.get(Byte.class)), (byte) (0x10)); + + final PacketContainer rotationPacket = new PacketContainer(PacketType.Play.Server.ENTITY_HEAD_ROTATION); + + rotationPacket.getIntegers().write(0, this.armorStandId); + rotationPacket.getBytes().write(0, (byte) (location.getYaw() * 256 / 360)); + + metaContainer.getIntegers().write(0, this.armorStandId); + metaContainer.getWatchableCollectionModifier().write(0, metaData.getWatchableObjects()); + + final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + final PacketContainer teleportPacket = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); + + teleportPacket.getIntegers().write(0, this.armorStandId); + teleportPacket.getDoubles(). + write(0, location.getX()). + write(1, location.getY()). + write(2, location.getZ()); + + teleportPacket.getBytes(). + write(0, (byte) (location.getYaw() * 256.0F / 360.0F)). + write(1, (byte) (location.getPitch() * 256.0F / 360.0F)); + + for (final Player p : Bukkit.getOnlinePlayers()) { + try { + protocolManager.sendServerPacket(p, armorPacket); + protocolManager.sendServerPacket(p, metaContainer); + protocolManager.sendServerPacket(p, rotationPacket); + } catch (final InvocationTargetException exception) { + exception.printStackTrace(); + } } - - if (this.attached == null) { - this.attached = player.getWorld().spawn(player.getLocation(), - ArmorStand.class, - armorStand -> { - armorStand.setVisible(false); - armorStand.setMarker(true); - armorStand.getPersistentDataContainer().set( - Keys.ARMOR_STAND_KEY, - PersistentDataType.BYTE, - (byte) 1 - ); - player.addPassenger(armorStand); - }); - } - - if (!player.getPassengers().contains(this.attached)) { - player.addPassenger(this.attached); - } - - final EntityEquipment equipment = this.attached.getEquipment(); - - if (equipment == null) { - this.despawnAttached(); - return; - } - - if (!backpackItem.equals(equipment.getHelmet())) { - equipment.setHelmet(backpackItem); - } - - this.attached. - setRotation( - player.getLocation().getYaw(), - player.getLocation().getPitch()); } public void despawnAttached() { - if (this.attached == null) { - return; + final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + final PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); + destroyPacket.getModifier().write(0, new IntArrayList(new int[]{this.armorStandId})); + + for (final Player p : Bukkit.getOnlinePlayers()) { + try { + protocolManager.sendServerPacket(p, destroyPacket); + } catch (final InvocationTargetException exception) { + exception.printStackTrace(); + } } - - final Player player = this.getPlayer(); - - if (player != null) { - player.removePassenger(this.attached); - } - - this.attached.remove(); - this.attached = null; } public ArmorItem getLastSetItem() { diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java b/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java index 59466d9f..2b85542b 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java @@ -6,36 +6,23 @@ import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.events.PacketListener; -import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.EnumWrappers; import com.comphenix.protocol.wrappers.Pair; -import dev.triumphteam.gui.guis.GuiItem; import io.github.fisher2911.hmccosmetics.HMCCosmetics; -import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; import io.github.fisher2911.hmccosmetics.message.Placeholder; import io.github.fisher2911.hmccosmetics.util.Keys; -import io.github.fisher2911.hmccosmetics.util.builder.ColorBuilder; import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder; -import net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment; -import net.minecraft.world.entity.EnumItemSlot; -import net.minecraft.world.level.levelgen.HeightMap; import org.bukkit.Bukkit; -import org.bukkit.Color; import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitTask; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -43,9 +30,11 @@ import java.util.UUID; public class UserManager { + private int currentArmorStandId = Integer.MAX_VALUE; private final HMCCosmetics plugin; private final Map userMap = new HashMap<>(); + private final Map armorStandIdMap = new HashMap<>(); private BukkitTask teleportTask; @@ -56,22 +45,14 @@ public class UserManager { public void add(final Player player) { final UUID uuid = player.getUniqueId(); - this.userMap.put(uuid, new User(uuid, new PlayerArmor( - new ArmorItem( - new ItemStack(Material.AIR), - "", - new ArrayList<>(), - "", - ArmorItem.Type.HAT - ), - new ArmorItem( - new ItemStack(Material.AIR), - "", - new ArrayList<>(), - "", - ArmorItem.Type.BACKPACK - ) - ))); + final int armorStandId = this.currentArmorStandId; + final User user = new User( + uuid, + PlayerArmor.empty(), + armorStandId); + this.userMap.put(uuid, user); + this.armorStandIdMap.put(armorStandId, user); + this.currentArmorStandId--; } public Optional get(final UUID uuid) { @@ -83,8 +64,15 @@ public class UserManager { } public void remove(final UUID uuid) { - this.get(uuid).ifPresent(User::detach); - this.userMap.remove(uuid); + final User user = this.userMap.remove(uuid); + + if (user == null) return; + + user.removeAllCosmetics(); + this.setFakeHelmet(user); + user.despawnAttached(); + + this.armorStandIdMap.remove(user.getArmorStandId()); } public void startTeleportTask() { @@ -96,9 +84,16 @@ public class UserManager { ); } + public void resendCosmetics(final Player player) { + for (final User user : this.userMap.values()) { + user.spawnArmorStand(player); + } + } + private void registerPacketListener() { final ProtocolManager protocolManager = this.plugin.getProtocolManager(); - protocolManager.addPacketListener(new PacketAdapter(this.plugin, + protocolManager.addPacketListener(new PacketAdapter( + this.plugin, ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_EQUIPMENT) { @Override @@ -129,7 +124,6 @@ public class UserManager { final ItemStack hat = user.getPlayerArmor().getHat().getItemStack(); final ItemStack second = entry.getSecond(); - if (hat != null && hat .getType() != Material.AIR && second != null && @@ -145,13 +139,20 @@ public class UserManager { public void setFakeHelmet(final User user) { - final ItemStack hat = user.getPlayerArmor().getHat().getItemStack(); + ItemStack hat = user.getPlayerArmor().getHat().getItemStack(); final Player player = user.getPlayer(); if (player == null || hat == null) { return; } + if (hat.getType() == Material.AIR) { + final EntityEquipment equipment = player.getEquipment(); + if (equipment != null) { + hat = equipment.getHelmet() == null ? hat : equipment.getHelmet(); + } + } + final List> equipmentList = new ArrayList<>(); final Map placeholders = Map.of(Placeholder.ALLOWED, "true", @@ -180,7 +181,7 @@ public class UserManager { public void removeAll() { for (final var user : this.userMap.values()) { - user.detach(); + user.despawnAttached(); } this.userMap.clear(); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/util/StringUtils.java b/src/main/java/io/github/fisher2911/hmccosmetics/util/StringUtils.java index 8d343297..7158a31d 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/util/StringUtils.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/util/StringUtils.java @@ -1,15 +1,25 @@ package io.github.fisher2911.hmccosmetics.util; +import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.message.Adventure; +import io.github.fisher2911.hmccosmetics.papi.PAPIHook; import net.kyori.adventure.text.Component; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; import java.util.Map; public class StringUtils { + private static final HMCCosmetics plugin; + + static { + plugin = HMCCosmetics.getPlugin(HMCCosmetics.class); + } + /** - * - * @param message message being translated + * @param message message being translated * @param placeholders placeholders applied * @return message with placeholders applied */ @@ -21,9 +31,15 @@ public class StringUtils { return message; } + public static String applyPapiPlaceholders(@Nullable final Player player, final String message) { + if (plugin.isPapiEnabled()) { + return PAPIHook.parse(player, message); + } + + return message; + } /** - * * @param parsed message to be parsed * @return MiniMessage parsed string */ diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/util/builder/ItemBuilder.java b/src/main/java/io/github/fisher2911/hmccosmetics/util/builder/ItemBuilder.java index df640b84..6d6dde7f 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/util/builder/ItemBuilder.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/util/builder/ItemBuilder.java @@ -3,9 +3,11 @@ package io.github.fisher2911.hmccosmetics.util.builder; import io.github.fisher2911.hmccosmetics.message.Adventure; import io.github.fisher2911.hmccosmetics.util.StringUtils; import net.kyori.adventure.text.Component; +import net.minecraft.network.PacketListener; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -147,6 +149,38 @@ public class ItemBuilder { return this; } + public ItemBuilder papiPlaceholders(final Player player) { + this.lorePapiPlaceholders(player); + this.namePapiPlaceholders(player); + return this; + } + + private void lorePapiPlaceholders(final Player player) { + if (this.itemMeta == null) return; + final List newLore = new ArrayList<>(); + + final List lore = this.itemMeta.getLore(); + + if (lore == null) return; + + for (final String line : this.itemMeta.getLore()) { + newLore.add(StringUtils.applyPapiPlaceholders(player, line)); + } + + this.itemMeta.setLore(newLore); + } + + private void namePapiPlaceholders(final Player player) { + if (this.itemMeta == null) return; + + this.itemMeta.setDisplayName( + StringUtils.applyPapiPlaceholders( + player, + this.itemMeta.getDisplayName() + ) + ); + } + /** * @param unbreakable whether the ItemStack is unbreakable * @return this diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5be5dafd..79c89934 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,6 +4,7 @@ version: 1.0.3 api-version: 1.17 softdepend: - Multiverse + - PlaceholderAPI depend: - ProtocolLib permissions: