diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..e08a013b --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 8fb7db0b..1fff01b8 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -12,6 +12,7 @@ diff --git a/.idea/modules/common/HMCCosmetics.common.test.iml b/.idea/modules/common/HMCCosmetics.common.test.iml new file mode 100644 index 00000000..c163761f --- /dev/null +++ b/.idea/modules/common/HMCCosmetics.common.test.iml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 9a13f5e6..658ff7f3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,5 +102,9 @@ bukkit { default = BukkitPluginDescription.Permission.Default.OP description = "Permission to set other users' cosmetics." } + register("hmccosmetics.cmd.wardrobe") { + default = BukkitPluginDescription.Permission.Default.OP + description = "Permission to view the wardrobe" + } } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 47f1ba50..99d0a73a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,2 @@ -rootProject.name = "HMCCosmetics" \ No newline at end of file +rootProject.name = "HMCCosmetics" +include("common") diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java b/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java index b1e00dbe..c3eabf9c 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/HMCCosmetics.java @@ -15,12 +15,14 @@ import io.github.fisher2911.hmccosmetics.hook.item.ItemsAdderHook; import io.github.fisher2911.hmccosmetics.listener.ClickListener; import io.github.fisher2911.hmccosmetics.listener.CosmeticFixListener; import io.github.fisher2911.hmccosmetics.listener.JoinListener; +import io.github.fisher2911.hmccosmetics.listener.PlayerShiftListener; import io.github.fisher2911.hmccosmetics.listener.RespawnListener; import io.github.fisher2911.hmccosmetics.listener.TeleportListener; import io.github.fisher2911.hmccosmetics.message.MessageHandler; import io.github.fisher2911.hmccosmetics.message.Messages; import io.github.fisher2911.hmccosmetics.message.Translation; import io.github.fisher2911.hmccosmetics.user.UserManager; + import java.nio.file.Path; import java.nio.file.Paths; import java.sql.SQLException; @@ -28,6 +30,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; + import me.mattstudios.mf.base.CommandManager; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -105,7 +108,8 @@ public class HMCCosmetics extends JavaPlugin { new ClickListener(this), new TeleportListener(this), new RespawnListener(this), - new CosmeticFixListener(this) + new CosmeticFixListener(this), + new PlayerShiftListener(this) ). forEach( listener -> this.getServer().getPluginManager() 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 2a1bbd91..53ba7623 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/command/CosmeticsCommand.java @@ -9,6 +9,7 @@ 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.user.UserManager; +import io.github.fisher2911.hmccosmetics.user.Wardrobe; import io.github.fisher2911.hmccosmetics.util.StringUtils; import java.util.Map; @@ -191,6 +192,31 @@ public class CosmeticsCommand extends CommandBase { } } + @SubCommand("wardrobe") + @Permission(io.github.fisher2911.hmccosmetics.message.Permission.VIEW_WARDROBE) + public void openWardrobe(final Player player) { + final Optional optionalUser = this.plugin.getUserManager().get(player.getUniqueId()); + if (optionalUser.isEmpty()) return; + + final User user = optionalUser.get(); + + final Wardrobe wardrobe = user.getWardrobe(); + if (wardrobe.isActive()) { + this.messageHandler.sendMessage( + player, + Messages.WARDROBE_ALREADY_OPEN + ); + return; + } + + wardrobe.setActive(true); + Bukkit.getScheduler().runTaskAsynchronously( + this.plugin, + () -> wardrobe.spawnFakePlayer(player) + ); + this.cosmeticsMenu.openDefault(player); + } + private void setDyeColor(final String dyeColor, final ArmorItem armorItem, final CommandSender sender) { try { final java.awt.Color awtColor = java.awt.Color.decode(dyeColor); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/database/Database.java b/src/main/java/io/github/fisher2911/hmccosmetics/database/Database.java index bf28b548..5dc8fd8e 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/database/Database.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/database/Database.java @@ -11,11 +11,14 @@ import io.github.fisher2911.hmccosmetics.database.dao.UserDAO; import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; import io.github.fisher2911.hmccosmetics.user.User; + import java.sql.SQLException; import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; + +import io.github.fisher2911.hmccosmetics.user.Wardrobe; import org.bukkit.Bukkit; public class Database { @@ -25,7 +28,7 @@ public class Database { final Dao armorItemDao; private final ConnectionSource dataSource; private final DatabaseType databaseType; - AtomicInteger ARMOR_STAND_ID = new AtomicInteger(Integer.MAX_VALUE); + AtomicInteger FAKE_ENTITY_ID = new AtomicInteger(Integer.MAX_VALUE); String TABLE_NAME = "user"; String PLAYER_UUID_COLUMN = "uuid"; String BACKPACK_COLUMN = "backpack"; @@ -67,7 +70,8 @@ public class Database { } public void loadUser(final UUID uuid, final Consumer onComplete) { - final int armorStandId = ARMOR_STAND_ID.getAndDecrement(); + final int armorStandId = FAKE_ENTITY_ID.getAndDecrement(); + final Wardrobe wardrobe = this.newWardrobe(); Threads.getInstance().execute( () -> { try { @@ -80,8 +84,11 @@ public class Database { final List armorItems = this.armorItemDao.queryForEq("uuid", uuid.toString()); - final User actualUser = user.toUser(this.plugin.getCosmeticManager(), - armorItems, armorStandId); + final User actualUser = user.toUser( + this.plugin.getCosmeticManager(), + armorItems, + wardrobe, + armorStandId); Bukkit.getScheduler().runTask(this.plugin, () -> { this.plugin.getUserManager().add( @@ -96,7 +103,7 @@ public class Database { } }); - final User user = new User(uuid, PlayerArmor.empty(), armorStandId); + final User user = new User(uuid, PlayerArmor.empty(), wardrobe, armorStandId); this.plugin.getUserManager().add(user); onComplete.accept(user); @@ -149,4 +156,13 @@ public class Database { return armorItemDao; } + public Wardrobe newWardrobe() { + return new Wardrobe( + UUID.randomUUID(), + PlayerArmor.empty(), + FAKE_ENTITY_ID.getAndDecrement(), + FAKE_ENTITY_ID.getAndDecrement(), + false + ); + } } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/database/DatabaseConverter.java b/src/main/java/io/github/fisher2911/hmccosmetics/database/DatabaseConverter.java index fed19f51..e2036b31 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/database/DatabaseConverter.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/database/DatabaseConverter.java @@ -4,6 +4,7 @@ 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.user.User; + import java.io.File; import java.io.IOException; import java.nio.file.Path; @@ -16,6 +17,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; + import org.bukkit.configuration.file.YamlConfiguration; public class DatabaseConverter { @@ -85,11 +87,12 @@ public class DatabaseConverter { while (results.next()) { final PlayerArmor playerArmor = PlayerArmor.empty(); - final User user = new User - (UUID.fromString(results.getString(1)), - playerArmor, - this.database.ARMOR_STAND_ID.getAndDecrement() - ); + final User user = new User( + UUID.fromString(results.getString(1)), + playerArmor, + this.database.newWardrobe(), + this.database.FAKE_ENTITY_ID.getAndDecrement() + ); final String backpackId = results.getString(2); final String hatId = results.getString(3); final int hatDye = results.getInt(4); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/database/dao/UserDAO.java b/src/main/java/io/github/fisher2911/hmccosmetics/database/dao/UserDAO.java index d6535891..7d7e39ac 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/database/dao/UserDAO.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/database/dao/UserDAO.java @@ -6,6 +6,8 @@ import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager; import io.github.fisher2911.hmccosmetics.gui.ArmorItem; import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; import io.github.fisher2911.hmccosmetics.user.User; +import io.github.fisher2911.hmccosmetics.user.Wardrobe; + import java.util.List; import java.util.Objects; import java.util.UUID; @@ -27,7 +29,10 @@ public class UserDAO { this.uuid = uuid; } - public User toUser(final CosmeticManager cosmeticManager, final List armorItems, + public User toUser( + final CosmeticManager cosmeticManager, + final List armorItems, + final Wardrobe wardrobe, final int armorStandId) { final PlayerArmor playerArmor = PlayerArmor.empty(); @@ -39,7 +44,7 @@ public class UserDAO { playerArmor.setItem(armorItem); } - return new User(this.uuid, playerArmor, armorStandId); + return new User(this.uuid, playerArmor, wardrobe, armorStandId); } @Override 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 684df993..519f1a7b 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/listener/ClickListener.java @@ -19,8 +19,6 @@ public class ClickListener implements Listener { private final HMCCosmetics plugin; private final UserManager userManager; - private final List equipmentSlots = List.of(39, 40); - public ClickListener(final HMCCosmetics plugin) { this.plugin = plugin; this.userManager = this.plugin.getUserManager(); diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/listener/JoinListener.java b/src/main/java/io/github/fisher2911/hmccosmetics/listener/JoinListener.java index e9136e21..a2c0c169 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/listener/JoinListener.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/listener/JoinListener.java @@ -2,7 +2,9 @@ package io.github.fisher2911.hmccosmetics.listener; import io.github.fisher2911.hmccosmetics.HMCCosmetics; import io.github.fisher2911.hmccosmetics.database.Database; +import io.github.fisher2911.hmccosmetics.user.User; import io.github.fisher2911.hmccosmetics.user.UserManager; +import io.github.fisher2911.hmccosmetics.user.Wardrobe; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -10,6 +12,8 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +import java.util.Optional; + public class JoinListener implements Listener { private final HMCCosmetics plugin; @@ -33,6 +37,17 @@ public class JoinListener implements Listener { @EventHandler public void onQuit(final PlayerQuitEvent event) { final Player player = event.getPlayer(); + final Optional optionalUser = this.userManager.get(player.getUniqueId()); + optionalUser.ifPresent(user -> { + final Wardrobe wardrobe = user.getWardrobe(); + + if (wardrobe.isActive()) { + Bukkit.getScheduler().runTaskAsynchronously( + this.plugin, + () -> wardrobe.despawnFakePlayer(player) + ); + } + }); this.userManager.remove(player.getUniqueId()); } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/listener/PlayerShiftListener.java b/src/main/java/io/github/fisher2911/hmccosmetics/listener/PlayerShiftListener.java new file mode 100644 index 00000000..f9228307 --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/listener/PlayerShiftListener.java @@ -0,0 +1,43 @@ +package io.github.fisher2911.hmccosmetics.listener; + +import io.github.fisher2911.hmccosmetics.HMCCosmetics; +import io.github.fisher2911.hmccosmetics.message.Messages; +import io.github.fisher2911.hmccosmetics.user.User; +import io.github.fisher2911.hmccosmetics.user.UserManager; +import io.github.fisher2911.hmccosmetics.user.Wardrobe; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerToggleSneakEvent; + +import java.util.Optional; + +public class PlayerShiftListener implements Listener { + + private final HMCCosmetics plugin; + private final UserManager userManager; + + public PlayerShiftListener(final HMCCosmetics plugin) { + this.plugin = plugin; + this.userManager = this.plugin.getUserManager(); + } + + @EventHandler + public void onPlayerShift(final PlayerToggleSneakEvent event) { + final Player player = event.getPlayer(); + final Optional userOptional = this.userManager.get(player.getUniqueId()); + + if (userOptional.isEmpty()) return; + + final User user = userOptional.get(); + final Wardrobe wardrobe = user.getWardrobe(); + + if (!wardrobe.isActive()) return; + + wardrobe.setActive(false); + this.plugin.getMessageHandler().sendMessage( + player, + Messages.CLOSED_WARDROBE + ); + } +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/message/Messages.java b/src/main/java/io/github/fisher2911/hmccosmetics/message/Messages.java index a03aaa3f..ff54bebd 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/message/Messages.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/message/Messages.java @@ -50,6 +50,13 @@ public class Messages { """); + public static final Message OPENED_WARDROBE = + new Message("opened-wardrobe", ChatColor.GREEN + "Viewing wardrobe!"); + public static final Message CLOSED_WARDROBE = + new Message("closed-wardrobe", ChatColor.GREEN + "Closing wardrobe!"); + public static final Message WARDROBE_ALREADY_OPEN = + new Message("wardrobe-already-open", ChatColor.RED + "The wardrobe is already open!"); + public static final Message SET_OTHER_BACKPACK = new Message( "set-other-backpack", ChatColor.GREEN + "You have set the backpack of " + Placeholder.PLAYER + " to " + Placeholder.TYPE + "." diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/message/Permission.java b/src/main/java/io/github/fisher2911/hmccosmetics/message/Permission.java index 17cfd0ff..fcde6639 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/message/Permission.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/message/Permission.java @@ -7,5 +7,6 @@ public class Permission { public static final String RELOAD_COMMAND = "hmccosmetics.cmd.reload"; public static final String HELP_COMMAND = "hmccosmetics.cmd.help"; public static final String SET_COSMETIC_COMMAND = "hmccosmetics.cmd.set"; + public static final String VIEW_WARDROBE = "hmccosmetics.cmd.wardrobe"; } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/packet/PacketManager.java b/src/main/java/io/github/fisher2911/hmccosmetics/packet/PacketManager.java index d9e97b78..d2d90c87 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/packet/PacketManager.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/packet/PacketManager.java @@ -4,21 +4,45 @@ 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.reflect.StructureModifier; import com.comphenix.protocol.wrappers.EnumWrappers; import com.comphenix.protocol.wrappers.MinecraftKey; import com.comphenix.protocol.wrappers.Pair; -import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.UUID; +import com.comphenix.protocol.wrappers.PlayerInfoData; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.mojang.authlib.GameProfile; +import io.github.fisher2911.hmccosmetics.playerpackets.PlayerPackets; +import io.github.fisher2911.hmccosmetics.playerpackets.PlayerPackets_1_17_R1; +import io.github.fisher2911.hmccosmetics.playerpackets.PlayerPackets_1_18_R1; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.IntArrayList; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + public class PacketManager { + private static final PlayerPackets playerPackets; + + static { + final String version = Bukkit.getVersion(); + if (version.contains("1.17")) { + playerPackets = new PlayerPackets_1_17_R1(); + } /*else if (version.contains("1.18")) { + playerPackets = new PlayerPackets_1_18_R1(); + }*/ else { + playerPackets = null; + } + } + public static PacketContainer getEntitySpawnPacket(final Location location, final int entityId, final EntityType entityType) { final PacketContainer packet = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); @@ -91,8 +115,6 @@ public class PacketManager { final float pitch, final EnumWrappers.SoundCategory soundCategory ) { -// final Location location = player.getLocation(); - final var manager = ProtocolLibrary.getProtocolManager(); final var packet = manager.createPacket(PacketType.Play.Server.CUSTOM_SOUND_EFFECT); @@ -119,6 +141,16 @@ public class PacketManager { return packet; } + public static PacketContainer[] getFakePlayerPacket(final Location location, Player player, final UUID uuid, final int entityId) throws IllegalStateException { + if (playerPackets == null) throw new IllegalStateException("This cannot be used in version: " + Bukkit.getVersion()); + return playerPackets.getSpawnPacket(location, player, uuid, entityId); + } + + public static PacketContainer getRemovePlayerPacket(final Player player, final UUID uuid, final int entityId) { + if (playerPackets == null) throw new IllegalStateException("This cannot be used in version: " + Bukkit.getVersion()); + return playerPackets.getRemovePacket(player, uuid, entityId); + } + public static void sendPacket(final Player to, final PacketContainer... packets) { final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); try { diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets.java b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets.java new file mode 100644 index 00000000..890b7bcc --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets.java @@ -0,0 +1,14 @@ +package io.github.fisher2911.hmccosmetics.playerpackets; + +import com.comphenix.protocol.events.PacketContainer; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public interface PlayerPackets { + + PacketContainer[] getSpawnPacket(final Location location, final Player player, final UUID uuid, final int entityId); + PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId); + +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_17_R1.java b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_17_R1.java new file mode 100644 index 00000000..9151beb1 --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_17_R1.java @@ -0,0 +1,87 @@ +package io.github.fisher2911.hmccosmetics.playerpackets; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.wrappers.EnumWrappers; +import com.comphenix.protocol.wrappers.PlayerInfoData; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.mojang.authlib.GameProfile; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class PlayerPackets_1_17_R1 implements PlayerPackets { + + public PacketContainer[] getSpawnPacket(final Location location, final Player player, final UUID uuid, final int entityId) { + final PacketContainer playerInfoPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); + final StructureModifier action = playerInfoPacket.getPlayerInfoAction(); + final StructureModifier> infoData = playerInfoPacket.getPlayerInfoDataLists(); + + final List playerInfoData = new ArrayList<>(); + + final GameProfile profile = this.getCopyProfile(player, uuid); + + playerInfoData.add(new PlayerInfoData(WrappedGameProfile + .fromHandle(profile), + 0, + EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE), + WrappedChatComponent.fromText(""))); + + action.write(0, EnumWrappers.PlayerInfoAction.ADD_PLAYER); + infoData.write(0, playerInfoData); + + final PacketContainer spawnPacket = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN); + spawnPacket.getUUIDs().write(0, uuid); + spawnPacket.getIntegers().write(0, entityId); + spawnPacket.getDoubles(). + write(0, location.getX()). + write(1, location.getY()). + write(2, location.getZ()); + + return new PacketContainer[]{playerInfoPacket, spawnPacket}; + } + + public PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId) { + final PacketContainer playerPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); + playerPacket.getIntegers().write(0, entityId); + final StructureModifier action = playerPacket.getPlayerInfoAction(); + final StructureModifier> infoData = playerPacket.getPlayerInfoDataLists(); + + final List playerInfoData = new ArrayList<>(); + + final GameProfile profile = this.getCopyProfile(player, uuid); + + playerInfoData.add(new PlayerInfoData(WrappedGameProfile + .fromHandle(profile), + 0, + EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE), + WrappedChatComponent.fromText(profile.getName()))); + + action.write(0, EnumWrappers.PlayerInfoAction.REMOVE_PLAYER); + infoData.write(0, playerInfoData); + + return playerPacket; + } + + private GameProfile getCopyProfile(final Player player, final UUID uuid) { + final GameProfile playerProfile = ((CraftPlayer) player).getProfile(); + final GameProfile profile = new GameProfile( + uuid, + player.getDisplayName()); + + for (final var entry : playerProfile.getProperties().entries()) { + profile.getProperties().put(entry.getKey(), entry.getValue()); + } + + return profile; + } + +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_18_R1.java b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_18_R1.java new file mode 100644 index 00000000..4851670a --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/playerpackets/PlayerPackets_1_18_R1.java @@ -0,0 +1,87 @@ +package io.github.fisher2911.hmccosmetics.playerpackets; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.wrappers.EnumWrappers; +import com.comphenix.protocol.wrappers.PlayerInfoData; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.mojang.authlib.GameProfile; +import org.bukkit.GameMode; +import org.bukkit.Location; +//import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class PlayerPackets_1_18_R1 implements PlayerPackets { + + public PacketContainer[] getSpawnPacket(final Location location, final Player player, final UUID uuid, final int entityId) { + final PacketContainer playerInfoPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); + final StructureModifier action = playerInfoPacket.getPlayerInfoAction(); + final StructureModifier> infoData = playerInfoPacket.getPlayerInfoDataLists(); + + final List playerInfoData = new ArrayList<>(); + + final GameProfile profile = this.getCopyProfile(player, uuid); + + playerInfoData.add(new PlayerInfoData(WrappedGameProfile + .fromHandle(profile), + 0, + EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE), + WrappedChatComponent.fromText(""))); + + action.write(0, EnumWrappers.PlayerInfoAction.ADD_PLAYER); + infoData.write(0, playerInfoData); + + final PacketContainer spawnPacket = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN); + spawnPacket.getUUIDs().write(0, uuid); + spawnPacket.getIntegers().write(0, entityId); + spawnPacket.getDoubles(). + write(0, location.getX()). + write(1, location.getY()). + write(2, location.getZ()); + + return new PacketContainer[]{playerInfoPacket, spawnPacket}; + } + + public PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId) { + final PacketContainer playerPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); + playerPacket.getIntegers().write(0, entityId); + final StructureModifier action = playerPacket.getPlayerInfoAction(); + final StructureModifier> infoData = playerPacket.getPlayerInfoDataLists(); + + final List playerInfoData = new ArrayList<>(); + + final GameProfile profile = this.getCopyProfile(player, uuid); + + playerInfoData.add(new PlayerInfoData(WrappedGameProfile + .fromHandle(profile), + 0, + EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE), + WrappedChatComponent.fromText(profile.getName()))); + + action.write(0, EnumWrappers.PlayerInfoAction.REMOVE_PLAYER); + infoData.write(0, playerInfoData); + + return playerPacket; + } + + private GameProfile getCopyProfile(final Player player, final UUID uuid) { + final GameProfile playerProfile = ((CraftPlayer) player).getProfile(); + final GameProfile profile = new GameProfile( + uuid, + player.getDisplayName()); + + for (final var entry : playerProfile.getProperties().entries()) { + profile.getProperties().put(entry.getKey(), entry.getValue()); + } + + return profile; + } + +} diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/user/Equipment.java b/src/main/java/io/github/fisher2911/hmccosmetics/user/Equipment.java new file mode 100644 index 00000000..10e107fc --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/Equipment.java @@ -0,0 +1,35 @@ +package io.github.fisher2911.hmccosmetics.user; + +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.Map; + +public class Equipment { + + private static final EquipmentSlot[] VALUES = EquipmentSlot.values(); + private final Map equipment = new EnumMap<>(EquipmentSlot.class); + + public Equipment() { + } + + public static Equipment fromEntityEquipment(final EntityEquipment entityEquipment) { + final Equipment equipment = new Equipment(); + for (final EquipmentSlot slot : VALUES) { + equipment.setItem(slot, entityEquipment.getItem(slot)); + } + return equipment; + } + + @Nullable + public ItemStack getItem(final EquipmentSlot slot) { + return this.equipment.get(slot); + } + + public void setItem(final EquipmentSlot slot, @Nullable final ItemStack itemStack) { + this.equipment.put(slot, itemStack); + } +} 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 deaae76b..2f38f60c 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/User.java @@ -27,12 +27,21 @@ public class User { private final UUID uuid; private final PlayerArmor playerArmor; + private Wardrobe wardrobe; + private ArmorItem lastSetItem = ArmorItem.empty(ArmorItem.Type.HAT); private boolean hasArmorStand; private final int armorStandId; - public User(final UUID uuid, final PlayerArmor playerArmor, final int armorStandId) { + public User(final UUID uuid, final PlayerArmor playerArmor, final Wardrobe wardrobe, final int armorStandId) { + this.uuid = uuid; + this.playerArmor = playerArmor; + this.wardrobe = wardrobe; + this.armorStandId = armorStandId; + } + + protected User(final UUID uuid, final PlayerArmor playerArmor, final int armorStandId) { this.uuid = uuid; this.playerArmor = playerArmor; this.armorStandId = armorStandId; @@ -50,6 +59,10 @@ public class User { return playerArmor; } + public Wardrobe getWardrobe() { + return wardrobe; + } + protected void setPlayerArmor(final PlayerArmor playerArmor) { for (final ArmorItem armorItem : playerArmor.getArmorItems()) { this.playerArmor.setItem(armorItem); @@ -162,4 +175,16 @@ public class User { public ArmorItem getLastSetItem() { return lastSetItem; } + + public int getEntityId() { + final Player player = this.getPlayer(); + if (player == null) return -1; + return player.getEntityId(); + } + + public boolean hasPermissionToUse(final ArmorItem armorItem) { + final Player player = this.getPlayer(); + if (player == null) return false; + return player.hasPermission(armorItem.getPermission()); + } } 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 91701d7e..c6dab9cc 100644 --- a/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java @@ -125,8 +125,11 @@ public class UserManager { public void updateCosmetics(final User user, final boolean ignoreRestrictions, final Player other) { final Player player = user.getPlayer(); + final Equipment equipment; if (player == null) { - return; + equipment = new Equipment(); + } else { + equipment = Equipment.fromEntityEquipment(player.getEquipment()); } final PlayerArmor playerArmor = user.getPlayerArmor(); @@ -134,28 +137,27 @@ public class UserManager { final List> equipmentList = new ArrayList<>(); equipmentList.add( - new Pair<>(EnumWrappers.ItemSlot.HEAD, this.getCosmeticItem(player, playerArmor.getHat(), EquipmentSlot.HEAD, ignoreRestrictions)) + new Pair<>(EnumWrappers.ItemSlot.HEAD, this.getCosmeticItem(equipment, playerArmor.getHat(), EquipmentSlot.HEAD, ignoreRestrictions)) ); equipmentList.add( - new Pair<>(EnumWrappers.ItemSlot.OFFHAND, this.getCosmeticItem(player, playerArmor.getOffHand(), EquipmentSlot.OFF_HAND, ignoreRestrictions)) + new Pair<>(EnumWrappers.ItemSlot.OFFHAND, this.getCosmeticItem(equipment, playerArmor.getOffHand(), EquipmentSlot.OFF_HAND, ignoreRestrictions)) ); PacketManager.sendPacket( other, PacketManager.getEquipmentPacket( equipmentList, - player.getEntityId() + user.getEntityId() ) ); } private ItemStack getCosmeticItem( - final Player player, + final Equipment equipment, final ArmorItem armorItem, final EquipmentSlot slot, final boolean ignoreRestrictions) { final CosmeticSettings cosmeticSettings = this.settings.getCosmeticSettings(); - final EntityEquipment equipment = player.getEquipment(); final Map placeholders = Map.of(Placeholder.ALLOWED, "true", Placeholder.ENABLED, "true"); @@ -180,17 +182,24 @@ public class UserManager { } public void setItem(final User user, final ArmorItem armorItem) { - ArmorItem previous = user.getPlayerArmor().getItem(armorItem.getType()); + final Wardrobe wardrobe = user.getWardrobe(); + final User setUser; + if (wardrobe.isActive()) { + setUser = wardrobe; + } else { + setUser = user; + } + ArmorItem previous = setUser.getPlayerArmor().getItem(armorItem.getType()); final CosmeticChangeEvent event = - new CosmeticChangeEvent(new CosmeticItem(armorItem.copy()), new CosmeticItem(previous.copy()), user); + new CosmeticChangeEvent(new CosmeticItem(armorItem.copy()), new CosmeticItem(previous.copy()), setUser); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) return; - user.setItem(event.getCosmeticItem().getArmorItem()); + setUser.setItem(event.getCosmeticItem().getArmorItem()); Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { switch (armorItem.getType()) { - case HAT, OFF_HAND -> this.updateCosmetics(user); + case HAT, OFF_HAND -> this.updateCosmetics(setUser); } }); } diff --git a/src/main/java/io/github/fisher2911/hmccosmetics/user/Wardrobe.java b/src/main/java/io/github/fisher2911/hmccosmetics/user/Wardrobe.java new file mode 100644 index 00000000..1b97fcdc --- /dev/null +++ b/src/main/java/io/github/fisher2911/hmccosmetics/user/Wardrobe.java @@ -0,0 +1,48 @@ +package io.github.fisher2911.hmccosmetics.user; + +import com.mojang.authlib.GameProfile; +import io.github.fisher2911.hmccosmetics.gui.ArmorItem; +import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor; +import io.github.fisher2911.hmccosmetics.packet.PacketManager; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class Wardrobe extends User { + + private final int entityId; + private boolean active; + + public Wardrobe( + final UUID uuid, + final PlayerArmor playerArmor, + final int armorStandId, + final int entityId, + final boolean active) { + super(uuid, playerArmor, armorStandId); + this.entityId = entityId; + this.active = active; + } + + public void spawnFakePlayer(final Player viewer) { + PacketManager.sendPacket(viewer, PacketManager.getFakePlayerPacket(viewer.getLocation(), viewer, this.getUuid(), this.entityId)); + } + + public void despawnFakePlayer(final Player viewer) { + PacketManager.sendPacket(viewer, PacketManager.getRemovePlayerPacket(viewer, this.getUuid(), this.entityId)); + } + + @Override + public boolean hasPermissionToUse(final ArmorItem armorItem) { + return true; + } + + public boolean isActive() { + return active; + } + + public void setActive(final boolean active) { + this.active = active; + } +}