diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java index d0cb1938..068ff039 100644 --- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java @@ -82,6 +82,11 @@ private void onApolloRegister(ApolloRegisterPlayerEvent event) { for (CommandCosmetic spec : npc.getCosmetics()) { cosmeticExample.equipNpcCosmeticToViewer(player, npc.getUuid(), spec); } + + PlayerNpc.ActiveEmote emote = npc.getActiveEmote(); + if (emote != null && npc.getViewers().contains(player.getUniqueId())) { + cosmeticExample.startNpcEmoteToViewer(player, npc.getUuid(), emote.getEmoteId(), emote.getMetadata()); + } } } diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java index 0c82c2ca..98608354 100644 --- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java @@ -175,13 +175,30 @@ public void startNpcEmoteExample(Player viewer, UUID npcUuid) { } @Override - public void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int metadata) { + public void startNpcEmoteInternal(UUID npcUuid, int emoteId, int metadata) { + List viewers = this.getApolloViewers(npcUuid); + if (viewers.isEmpty()) { + return; + } + Emote emote = Emote.builder() .id(emoteId) .metadata(metadata) .build(); - this.cosmeticModule.startNpcEmote(Recipients.ofEveryone(), npcUuid, emote); + this.cosmeticModule.startNpcEmote(Recipients.of(viewers), npcUuid, emote); + } + + @Override + public void startNpcEmoteToViewer(Player viewer, UUID npcUuid, int emoteId, int metadata) { + Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()).ifPresent(apolloPlayer -> { + Emote emote = Emote.builder() + .id(emoteId) + .metadata(metadata) + .build(); + + this.cosmeticModule.startNpcEmote(apolloPlayer, npcUuid, emote); + }); } @Override @@ -189,6 +206,31 @@ public void stopNpcEmoteExample(Player viewer, UUID npcUuid) { this.cosmeticModule.stopNpcEmote(Recipients.ofEveryone(), npcUuid); } + @Override + public void stopNpcEmoteInternal(UUID npcUuid) { + List viewers = this.getApolloViewers(npcUuid); + if (viewers.isEmpty()) { + return; + } + + this.cosmeticModule.stopNpcEmote(Recipients.of(viewers), npcUuid); + } + + @Override + public void stopNpcEmoteToViewer(Player viewer, UUID npcUuid) { + Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()).ifPresent(apolloPlayer -> + this.cosmeticModule.stopNpcEmote(apolloPlayer, npcUuid)); + } + + private List getApolloViewers(UUID npcUuid) { + List viewers = new ArrayList<>(); + for (Player player : this.getNpcViewers(npcUuid)) { + Apollo.getPlayerManager().getPlayer(player.getUniqueId()).ifPresent(viewers::add); + } + + return viewers; + } + @Override public void resetNpcEmotesExample() { this.cosmeticModule.resetNpcEmotes(Recipients.ofEveryone()); diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java index c39d80f2..bbc87c31 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java @@ -91,6 +91,7 @@ import com.lunarclient.apollo.example.module.impl.VignetteExample; import com.lunarclient.apollo.example.module.impl.WaypointExample; import com.lunarclient.apollo.example.nms.NpcManager; +import com.lunarclient.apollo.example.nms.PlayerNpc; import lombok.Getter; import lombok.Setter; import org.bukkit.plugin.java.JavaPlugin; @@ -150,6 +151,13 @@ public void onEnable() { this.registerCommands(); this.registerModuleExamples(); this.registerListeners(); + + this.npcManager.addViewerListener((viewer, npc) -> { + PlayerNpc.ActiveEmote emote = npc.getActiveEmote(); + if (emote != null && this.cosmeticExample != null) { + this.cosmeticExample.startNpcEmoteToViewer(viewer, npc.getUuid(), emote.getEmoteId(), emote.getMetadata()); + } + }); } @Override diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java index ad8057e8..92817e47 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java @@ -222,7 +222,12 @@ private boolean handleEmote(Player player, CosmeticExample example, String[] arg } } - example.startNpcEmoteInternal(player, uuid, emoteId, metadata); + example.startNpcEmoteInternal(uuid, emoteId, metadata); + + int emoteMetadata = metadata; + ApolloExamplePlugin.getInstance().getNpcManager().findByUuid(uuid) + .ifPresent(npc -> npc.setActiveEmote(new PlayerNpc.ActiveEmote(emoteId, emoteMetadata))); + player.sendMessage(ChatColor.GREEN + "Started emote " + emoteId + " on NPC " + args[2]); break; } @@ -238,13 +243,22 @@ private boolean handleEmote(Player player, CosmeticExample example, String[] arg return true; } - example.stopNpcEmoteExample(player, uuid); + example.stopNpcEmoteInternal(uuid); + + ApolloExamplePlugin.getInstance().getNpcManager().findByUuid(uuid) + .ifPresent(npc -> npc.setActiveEmote(null)); + player.sendMessage(ChatColor.GREEN + "Stopped emote on NPC " + args[2]); break; } case "reset": { example.resetNpcEmotesExample(); + + for (PlayerNpc npc : ApolloExamplePlugin.getInstance().getNpcManager().getNpcs()) { + npc.setActiveEmote(null); + } + player.sendMessage(ChatColor.GREEN + "Reset all NPC emotes"); break; } diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java index 0e56b672..b6e33e20 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java @@ -23,6 +23,7 @@ */ package com.lunarclient.apollo.example.module.impl; +import com.lunarclient.apollo.example.ApolloExamplePlugin; import com.lunarclient.apollo.example.module.ApolloModuleExample; import com.lunarclient.apollo.example.nms.CommandCosmetic; import java.util.Collections; @@ -54,12 +55,30 @@ public void equipNpcCosmeticToViewer(Player viewer, UUID npcUuid, CommandCosmeti public abstract void startNpcEmoteExample(Player viewer, UUID npcUuid); - public abstract void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int metadata); + public void startNpcEmoteInternal(UUID npcUuid, int emoteId, int metadata) { + for (Player viewer : this.getNpcViewers(npcUuid)) { + this.startNpcEmoteToViewer(viewer, npcUuid, emoteId, metadata); + } + } + + public abstract void startNpcEmoteToViewer(Player viewer, UUID npcUuid, int emoteId, int metadata); public abstract void stopNpcEmoteExample(Player viewer, UUID npcUuid); + public void stopNpcEmoteInternal(UUID npcUuid) { + for (Player viewer : this.getNpcViewers(npcUuid)) { + this.stopNpcEmoteToViewer(viewer, npcUuid); + } + } + + public abstract void stopNpcEmoteToViewer(Player viewer, UUID npcUuid); + public abstract void resetNpcEmotesExample(); + protected List getNpcViewers(UUID npcUuid) { + return ApolloExamplePlugin.getInstance().getNpcManager().getViewers(npcUuid); + } + public abstract void displaySprayExample(Player viewer, int sprayId); public abstract void removeSprayExample(int sprayId); diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/listener/ApolloPlayerJsonListener.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/listener/ApolloPlayerJsonListener.java index 6c0984d5..217aab0a 100644 --- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/listener/ApolloPlayerJsonListener.java +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/listener/ApolloPlayerJsonListener.java @@ -26,6 +26,9 @@ import com.google.gson.JsonObject; import com.lunarclient.apollo.example.ApolloExamplePlugin; import com.lunarclient.apollo.example.json.util.JsonPacketUtil; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.example.nms.CommandCosmetic; +import com.lunarclient.apollo.example.nms.PlayerNpc; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -81,6 +84,26 @@ private void onRegisterChannel(PlayerRegisterChannelEvent event) { PLAYERS_RUNNING_APOLLO.add(player.getUniqueId()); player.sendMessage("You are using LunarClient!"); + + this.applyNpcCosmetics(player); + } + + private void applyNpcCosmetics(Player player) { + CosmeticExample cosmeticExample = this.plugin.getCosmeticExample(); + if (cosmeticExample == null) { + return; + } + + for (PlayerNpc npc : this.plugin.getNpcManager().getNpcs()) { + for (CommandCosmetic spec : npc.getCosmetics()) { + cosmeticExample.equipNpcCosmeticToViewer(player, npc.getUuid(), spec); + } + + PlayerNpc.ActiveEmote emote = npc.getActiveEmote(); + if (emote != null && npc.getViewers().contains(player.getUniqueId())) { + cosmeticExample.startNpcEmoteToViewer(player, npc.getUuid(), emote.getEmoteId(), emote.getMetadata()); + } + } } @EventHandler diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java index cff28169..b9a43ef9 100644 --- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java @@ -29,6 +29,7 @@ import com.lunarclient.apollo.example.json.util.JsonPacketUtil; import com.lunarclient.apollo.example.json.util.JsonUtil; import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.example.nms.CommandCosmetic; import java.time.Duration; import java.util.List; import java.util.UUID; @@ -103,6 +104,56 @@ public void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List JsonPacketUtil.broadcastPacket(message); } + @Override + public void equipNpcCosmeticInternal(Player viewer, UUID npcUuid, CommandCosmetic cosmetic) { + JsonPacketUtil.broadcastPacket(this.createEquipMessage(npcUuid, cosmetic)); + } + + @Override + public void equipNpcCosmeticToViewer(Player viewer, UUID npcUuid, CommandCosmetic cosmetic) { + JsonPacketUtil.sendPacket(viewer, this.createEquipMessage(npcUuid, cosmetic)); + } + + private JsonObject createEquipMessage(UUID npcUuid, CommandCosmetic cosmetic) { + JsonObject cosmeticObject = new JsonObject(); + cosmeticObject.addProperty("id", cosmetic.getId()); + + CommandCosmetic.Options options = cosmetic.getOptions(); + if (options instanceof CommandCosmetic.Hat) { + CommandCosmetic.Hat hat = (CommandCosmetic.Hat) options; + JsonObject hatOptions = new JsonObject(); + hatOptions.addProperty("show_over_helmet", hat.isShowOverHelmet()); + hatOptions.addProperty("show_over_skin_layer", hat.isShowOverSkinLayer()); + hatOptions.addProperty("height_offset", hat.getHeightOffset()); + cosmeticObject.add("hat_options", hatOptions); + } else if (options instanceof CommandCosmetic.Cloak) { + JsonObject cloakOptions = new JsonObject(); + cloakOptions.addProperty("use_cloth_physics", ((CommandCosmetic.Cloak) options).isUseClothPhysics()); + cosmeticObject.add("cloak_options", cloakOptions); + } else if (options instanceof CommandCosmetic.Pet) { + JsonObject petOptions = new JsonObject(); + petOptions.addProperty("flip_shoulder", ((CommandCosmetic.Pet) options).isFlipShoulder()); + cosmeticObject.add("pet_options", petOptions); + } else if (options instanceof CommandCosmetic.Body) { + CommandCosmetic.Body body = (CommandCosmetic.Body) options; + JsonObject bodyOptions = new JsonObject(); + bodyOptions.addProperty("show_over_chestplate", body.isShowOverChestplate()); + bodyOptions.addProperty("show_over_leggings", body.isShowOverLeggings()); + bodyOptions.addProperty("show_over_boots", body.isShowOverBoots()); + cosmeticObject.add("body_options", bodyOptions); + } + + JsonArray cosmeticsArray = new JsonArray(); + cosmeticsArray.add(cosmeticObject); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetics", cosmeticsArray); + + return message; + } + @Override public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); @@ -154,7 +205,7 @@ public void startNpcEmoteExample(Player viewer, UUID npcUuid) { } @Override - public void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int metadata) { + public void startNpcEmoteToViewer(Player viewer, UUID npcUuid, int emoteId, int metadata) { JsonObject emote = new JsonObject(); emote.addProperty("id", emoteId); emote.addProperty("metadata", metadata); @@ -164,7 +215,7 @@ public void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); message.add("emote", emote); - JsonPacketUtil.broadcastPacket(message); + JsonPacketUtil.sendPacket(viewer, message); } @Override @@ -176,6 +227,15 @@ public void stopNpcEmoteExample(Player viewer, UUID npcUuid) { JsonPacketUtil.broadcastPacket(message); } + @Override + public void stopNpcEmoteToViewer(Player viewer, UUID npcUuid) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.StopNpcEmoteMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + + JsonPacketUtil.sendPacket(viewer, message); + } + @Override public void resetNpcEmotesExample() { JsonObject message = new JsonObject(); diff --git a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java index be00b996..37f2e2e5 100644 --- a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java +++ b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java @@ -26,6 +26,7 @@ import com.mojang.authlib.GameProfile; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -51,10 +52,12 @@ import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Nullable; @@ -65,7 +68,11 @@ public final class NpcManager implements Listener { HumanoidArm.RIGHT, false, false, ParticleStatus.ALL ); + private static final double TRACKING_RANGE = 48.0; + private static final double TRACKING_RANGE_SQUARED = TRACKING_RANGE * TRACKING_RANGE; + private final Map npcs = new HashMap<>(); + private final List viewerListeners = new ArrayList<>(); private final JavaPlugin plugin; private final NpcStore store; @@ -75,6 +82,11 @@ public NpcManager(JavaPlugin plugin) { Bukkit.getPluginManager().registerEvents(this, plugin); Bukkit.getScheduler().runTask(plugin, this::loadOrSpawnDefaults); + Bukkit.getScheduler().runTaskTimer(plugin, this::updateVisibility, 1L, 10L); + } + + public void addViewerListener(NpcViewerListener listener) { + this.viewerListeners.add(listener); } public void removeNpc(UUID uuid) { @@ -114,21 +126,73 @@ public Collection getNpcs() { } @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - ServerPlayer viewer = ((CraftPlayer) event.getPlayer()).getHandle(); - for (PlayerNpc npc : this.npcs.values()) { - this.showNpc(viewer, npc); - } + public void onPlayerQuit(PlayerQuitEvent event) { + this.forgetViewer(event.getPlayer()); } @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - ServerPlayer viewer = ((CraftPlayer) event.getPlayer()).getHandle(); + public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { + this.forgetViewer(event.getPlayer()); + } + + @EventHandler + public void onPlayerRespawn(PlayerRespawnEvent event) { + this.forgetViewer(event.getPlayer()); + } + + private void forgetViewer(Player player) { for (PlayerNpc npc : this.npcs.values()) { - this.hideNpc(viewer, npc); + npc.getViewers().remove(player.getUniqueId()); + } + } + + private void updateVisibility() { + for (Player player : Bukkit.getOnlinePlayers()) { + for (PlayerNpc npc : this.npcs.values()) { + boolean inRange = this.isWithinTrackingRange(player, npc); + boolean viewing = npc.getViewers().contains(player.getUniqueId()); + + if (inRange && !viewing) { + npc.getViewers().add(player.getUniqueId()); + this.showNpc(((CraftPlayer) player).getHandle(), npc); + + for (NpcViewerListener listener : this.viewerListeners) { + listener.onNpcShown(player, npc); + } + } else if (!inRange && viewing) { + npc.getViewers().remove(player.getUniqueId()); + this.hideNpc(((CraftPlayer) player).getHandle(), npc); + } + } } } + private boolean isWithinTrackingRange(Player player, PlayerNpc npc) { + Location location = npc.getLocation(); + World world = location.getWorld(); + + return world != null + && world.equals(player.getWorld()) + && player.getLocation().distanceSquared(location) <= NpcManager.TRACKING_RANGE_SQUARED; + } + + public List getViewers(UUID npcUuid) { + PlayerNpc npc = this.npcs.get(npcUuid); + if (npc == null) { + return Collections.emptyList(); + } + + List viewers = new ArrayList<>(); + for (UUID viewerUuid : npc.getViewers()) { + Player player = Bukkit.getPlayer(viewerUuid); + if (player != null) { + viewers.add(player); + } + } + + return viewers; + } + private void loadOrSpawnDefaults() { if (!this.store.exists()) { this.spawnDefaultNpcs(); @@ -184,9 +248,7 @@ private void spawnDefaultNpcs() { PlayerNpc playerNpc = new PlayerNpc(npc.getUUID(), name, location.clone(), npc); this.npcs.put(playerNpc.getUuid(), playerNpc); - for (ServerPlayer viewer : server.getPlayerList().getPlayers()) { - this.showNpc(viewer, playerNpc); - } + this.updateVisibility(); return playerNpc; } @@ -220,9 +282,14 @@ private void hideNpc(ServerPlayer viewer, PlayerNpc npc) { } private void despawnNpcs(PlayerNpc npc) { - for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) { - this.hideNpc(player, npc); + for (UUID viewerUuid : npc.getViewers()) { + Player player = Bukkit.getPlayer(viewerUuid); + if (player != null) { + this.hideNpc(((CraftPlayer) player).getHandle(), npc); + } } + + npc.getViewers().clear(); } } diff --git a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcViewerListener.java b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcViewerListener.java new file mode 100644 index 00000000..479e3eeb --- /dev/null +++ b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcViewerListener.java @@ -0,0 +1,33 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2026 Moonsworth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.lunarclient.apollo.example.nms; + +import org.bukkit.entity.Player; + +@FunctionalInterface +public interface NpcViewerListener { + + void onNpcShown(Player viewer, PlayerNpc npc); + +} diff --git a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java index 9bdde6f9..fa464bc3 100644 --- a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java +++ b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java @@ -24,13 +24,16 @@ package com.lunarclient.apollo.example.nms; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import net.minecraft.server.level.ServerPlayer; import org.bukkit.Location; +import org.jetbrains.annotations.Nullable; @Getter @RequiredArgsConstructor @@ -40,12 +43,26 @@ public final class PlayerNpc { private final String name; private final Location location; private final ServerPlayer handle; + private final Set viewers = new HashSet<>(); @Setter private List cosmetics = new ArrayList<>(); + @Setter + @Nullable + private ActiveEmote activeEmote; + public int getEntityId() { return this.handle.getId(); } + @Getter + @RequiredArgsConstructor + public static final class ActiveEmote { + + private final int emoteId; + private final int metadata; + + } + } diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/listener/ApolloPlayerProtoListener.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/listener/ApolloPlayerProtoListener.java index a9aee632..6e55ce2a 100644 --- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/listener/ApolloPlayerProtoListener.java +++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/listener/ApolloPlayerProtoListener.java @@ -24,6 +24,9 @@ package com.lunarclient.apollo.example.proto.listener; import com.lunarclient.apollo.example.ApolloExamplePlugin; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.example.nms.CommandCosmetic; +import com.lunarclient.apollo.example.nms.PlayerNpc; import com.lunarclient.apollo.example.proto.util.ProtobufPacketUtil; import com.lunarclient.apollo.player.v1.UpdatePlayerWorldMessage; import java.util.HashSet; @@ -79,6 +82,26 @@ private void onRegisterChannel(PlayerRegisterChannelEvent event) { PLAYERS_RUNNING_APOLLO.add(player.getUniqueId()); player.sendMessage("You are using LunarClient!"); + + this.applyNpcCosmetics(player); + } + + private void applyNpcCosmetics(Player player) { + CosmeticExample cosmeticExample = this.plugin.getCosmeticExample(); + if (cosmeticExample == null) { + return; + } + + for (PlayerNpc npc : this.plugin.getNpcManager().getNpcs()) { + for (CommandCosmetic spec : npc.getCosmetics()) { + cosmeticExample.equipNpcCosmeticToViewer(player, npc.getUuid(), spec); + } + + PlayerNpc.ActiveEmote emote = npc.getActiveEmote(); + if (emote != null && npc.getViewers().contains(player.getUniqueId())) { + cosmeticExample.startNpcEmoteToViewer(player, npc.getUuid(), emote.getEmoteId(), emote.getMetadata()); + } + } } @EventHandler diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java index e83851d7..f66f03aa 100644 --- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java +++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java @@ -24,11 +24,13 @@ package com.lunarclient.apollo.example.proto.module; import com.google.common.collect.Lists; +import com.lunarclient.apollo.cosmetic.v1.BodyOptions; import com.lunarclient.apollo.cosmetic.v1.CloakOptions; import com.lunarclient.apollo.cosmetic.v1.Cosmetic; import com.lunarclient.apollo.cosmetic.v1.DisplaySprayMessage; import com.lunarclient.apollo.cosmetic.v1.Emote; import com.lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage; +import com.lunarclient.apollo.cosmetic.v1.HatOptions; import com.lunarclient.apollo.cosmetic.v1.PetOptions; import com.lunarclient.apollo.cosmetic.v1.RemoveSprayMessage; import com.lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage; @@ -38,6 +40,7 @@ import com.lunarclient.apollo.cosmetic.v1.StopNpcEmoteMessage; import com.lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage; import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.example.nms.CommandCosmetic; import com.lunarclient.apollo.example.proto.util.ProtobufPacketUtil; import com.lunarclient.apollo.example.proto.util.ProtobufUtil; import com.lunarclient.apollo.packetenrichment.v1.Direction; @@ -109,6 +112,51 @@ public void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List ProtobufPacketUtil.broadcastPacket(message); } + @Override + public void equipNpcCosmeticInternal(Player viewer, UUID npcUuid, CommandCosmetic cosmetic) { + ProtobufPacketUtil.broadcastPacket(this.createEquipMessage(npcUuid, cosmetic)); + } + + @Override + public void equipNpcCosmeticToViewer(Player viewer, UUID npcUuid, CommandCosmetic cosmetic) { + ProtobufPacketUtil.sendPacket(viewer, this.createEquipMessage(npcUuid, cosmetic)); + } + + private EquipNpcCosmeticsMessage createEquipMessage(UUID npcUuid, CommandCosmetic cosmetic) { + Cosmetic.Builder cosmeticBuilder = Cosmetic.newBuilder() + .setId(cosmetic.getId()); + + CommandCosmetic.Options options = cosmetic.getOptions(); + if (options instanceof CommandCosmetic.Hat) { + CommandCosmetic.Hat hat = (CommandCosmetic.Hat) options; + cosmeticBuilder.setHatOptions(HatOptions.newBuilder() + .setShowOverHelmet(hat.isShowOverHelmet()) + .setShowOverSkinLayer(hat.isShowOverSkinLayer()) + .setHeightOffset(hat.getHeightOffset()) + .build()); + } else if (options instanceof CommandCosmetic.Cloak) { + cosmeticBuilder.setCloakOptions(CloakOptions.newBuilder() + .setUseClothPhysics(((CommandCosmetic.Cloak) options).isUseClothPhysics()) + .build()); + } else if (options instanceof CommandCosmetic.Pet) { + cosmeticBuilder.setPetOptions(PetOptions.newBuilder() + .setFlipShoulder(((CommandCosmetic.Pet) options).isFlipShoulder()) + .build()); + } else if (options instanceof CommandCosmetic.Body) { + CommandCosmetic.Body body = (CommandCosmetic.Body) options; + cosmeticBuilder.setBodyOptions(BodyOptions.newBuilder() + .setShowOverChestplate(body.isShowOverChestplate()) + .setShowOverLeggings(body.isShowOverLeggings()) + .setShowOverBoots(body.isShowOverBoots()) + .build()); + } + + return EquipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addCosmetics(cosmeticBuilder.build()) + .build(); + } + @Override public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); @@ -153,7 +201,7 @@ public void startNpcEmoteExample(Player viewer, UUID npcUuid) { } @Override - public void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int metadata) { + public void startNpcEmoteToViewer(Player viewer, UUID npcUuid, int emoteId, int metadata) { StartNpcEmoteMessage message = StartNpcEmoteMessage.newBuilder() .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) .setEmote(Emote.newBuilder() @@ -162,7 +210,7 @@ public void startNpcEmoteInternal(Player viewer, UUID npcUuid, int emoteId, int .build()) .build(); - ProtobufPacketUtil.broadcastPacket(message); + ProtobufPacketUtil.sendPacket(viewer, message); } @Override @@ -174,6 +222,15 @@ public void stopNpcEmoteExample(Player viewer, UUID npcUuid) { ProtobufPacketUtil.broadcastPacket(message); } + @Override + public void stopNpcEmoteToViewer(Player viewer, UUID npcUuid) { + StopNpcEmoteMessage message = StopNpcEmoteMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + @Override public void resetNpcEmotesExample() { ResetNpcEmotesMessage message = ResetNpcEmotesMessage.getDefaultInstance();