From db6058bbd0507a9b4452ffdc80f11ee4c7f8b244 Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 28 Aug 2023 01:49:14 +0800 Subject: [PATCH] Reimplement hologram display entities using interaction entities as suggested by Owen1212055 --- .../main/java/net/citizensnpcs/Settings.java | 3 ++ .../citizensnpcs/commands/NPCCommands.java | 4 +-- .../npc/ai/BoundingBoxExaminer.java | 4 +-- .../net/citizensnpcs/trait/HologramTrait.java | 26 +++++++------- .../net/citizensnpcs/trait/MountTrait.java | 3 ++ .../waypoint/LinearWaypointProvider.java | 35 +++++++++++-------- .../main/java/net/citizensnpcs/util/NMS.java | 6 +++- .../java/net/citizensnpcs/util/NMSBridge.java | 5 ++- .../nms/v1_19_R3/util/NMSImpl.java | 25 +++++++++++++ .../nms/v1_20_R1/util/NMSImpl.java | 28 ++++++++++++++- 10 files changed, 105 insertions(+), 34 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java index 302ec217a..3e3a5b2f3 100644 --- a/main/src/main/java/net/citizensnpcs/Settings.java +++ b/main/src/main/java/net/citizensnpcs/Settings.java @@ -166,6 +166,9 @@ public class Settings { "Minecraft will pick a 'close-enough' location when pathfinding to a block if it can't find a direct path
Disabled by default", "npc.pathfinding.disable-mc-fallback-navigation", true), DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true), + DISPLAY_ENTITY_HOLOGRAMS( + "Whether to use display entities for holograms by default. In theory more performant than armor stands. Requires 1.19.4 or above. Defaults to false", + "npc.hologram.use-display-entities", false), ENTITY_SPAWN_WAIT_DURATION( "Entities are no longer spawned until the chunks are loaded from disk
Wait for chunk loading for one second by default, increase if your disk is slow", "general.entity-spawn-wait-ticks", "general.wait-for-entity-spawn", "1s"), diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 4b0dc250c..ab788233c 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -99,7 +99,7 @@ import net.citizensnpcs.api.util.SpigotUtil; import net.citizensnpcs.commands.gui.NPCConfigurator; import net.citizensnpcs.commands.history.CommandHistory; import net.citizensnpcs.commands.history.CreateNPCHistoryItem; -import net.citizensnpcs.commands.history.RemoveNPCHistoryItem; +import net.citizensnpcs.commands.history.RemoveNPCHistoryItem; import net.citizensnpcs.npc.EntityControllers; import net.citizensnpcs.npc.NPCSelector; import net.citizensnpcs.npc.Template; @@ -2797,7 +2797,7 @@ public class NPCCommands { Messaging.sendTr(sender, Messages.SITTING_SET, npc.getName(), Util.prettyPrintLocation(at)); } - @Command( + @Command( aliases = { "npc" }, usage = "skin (-e(xport) -c(lear) -l(atest)) [name] (or --url [url] --file [file] (-s(lim)) or -t [uuid/name] [data] [signature])", desc = "Sets an NPC's skin name. Use -l to set the skin to always update to the latest", diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/BoundingBoxExaminer.java b/main/src/main/java/net/citizensnpcs/npc/ai/BoundingBoxExaminer.java index b22cdfcd2..1b488e5ec 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/BoundingBoxExaminer.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/BoundingBoxExaminer.java @@ -39,9 +39,9 @@ public class BoundingBoxExaminer implements BlockExaminer { if (above == null || below == null) return PassableState.IGNORE; float height = (float) (above.minY - below.maxY); - if (height < this.height) { + if (height < this.height) return PassableState.UNPASSABLE; - } + } return PassableState.IGNORE; } diff --git a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java index 88d8bf343..0354a1f05 100644 --- a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java @@ -12,11 +12,10 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.entity.Display.Billboard; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Interaction; import org.bukkit.entity.Player; -import org.bukkit.entity.TextDisplay; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.inventory.ItemStack; @@ -55,7 +54,7 @@ public class HologramTrait extends Trait { private HologramLine nameLine; private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore()); private int t; - private boolean useTextDisplay; + private boolean useDisplayEntities = Setting.DISPLAY_ENTITY_HOLOGRAMS.asBoolean(); public HologramTrait() { super("hologramtrait"); @@ -99,9 +98,10 @@ public class HologramTrait extends Trait { @SuppressWarnings("deprecation") private NPC createHologram(String line, double heightOffset) { NPC hologramNPC = null; - if (useTextDisplay) { - hologramNPC = registry.createNPC(EntityType.TEXT_DISPLAY, line); + if (useDisplayEntities) { + hologramNPC = registry.createNPC(EntityType.INTERACTION, line); hologramNPC.addTrait(new ClickRedirectTrait(npc)); + hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, true); } else { hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, line); hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc); @@ -116,9 +116,9 @@ public class HologramTrait extends Trait { + (direction == HologramDirection.BOTTOM_UP ? heightOffset : getMaxHeight() - heightOffset), 0)); - if (useTextDisplay) { - ((TextDisplay) hologramNPC.getEntity()).setBillboard(Billboard.CENTER); - ((TextDisplay) hologramNPC.getEntity()).setInterpolationDelay(0); + if (useDisplayEntities) { + ((Interaction) hologramNPC.getEntity()).setInteractionWidth(0); + NMS.updateMountedInteractionHeight(hologramNPC.getEntity(), npc.getEntity(), heightOffset); } Matcher itemMatcher = ITEM_MATCHER.matcher(line); @@ -159,7 +159,7 @@ public class HologramTrait extends Trait { } private double getEntityHeight() { - return NMS.getHeight(npc.getEntity()) + (useTextDisplay ? 0.27 : 0); + return NMS.getHeight(npc.getEntity()); } private double getHeight(int lineNumber) { @@ -320,7 +320,7 @@ public class HologramTrait extends Trait { } if (nameLine != null && nameLine.hologram.isSpawned()) { - if (updatePosition) { + if (updatePosition && !useDisplayEntities) { nameLine.hologram.teleport(currentLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN); } if (updateName) { @@ -334,7 +334,7 @@ public class HologramTrait extends Trait { if (hologramNPC == null || !hologramNPC.isSpawned()) continue; - if (updatePosition) { + if (updatePosition && !useDisplayEntities) { Location tp = currentLoc.clone().add(0, lastEntityHeight + (direction == HologramDirection.BOTTOM_UP ? getHeight(i) : getMaxHeight() - getHeight(i)), 0); hologramNPC.teleport(tp, TeleportCause.PLUGIN); @@ -443,8 +443,8 @@ public class HologramTrait extends Trait { this.customHologramSupplier = nameSupplier; } - public void setUseTextDisplay(boolean use) { - this.useTextDisplay = use; + public void setUseDisplayEntities(boolean use) { + this.useDisplayEntities = use; reloadLineHolograms(); } diff --git a/main/src/main/java/net/citizensnpcs/trait/MountTrait.java b/main/src/main/java/net/citizensnpcs/trait/MountTrait.java index e58613193..578a078b8 100644 --- a/main/src/main/java/net/citizensnpcs/trait/MountTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/MountTrait.java @@ -71,6 +71,9 @@ public class MountTrait extends Trait { public void setMountedOn(UUID uuid) { this.uuid = uuid; + if (npc.isSpawned()) { + checkMounted(); + } } public void unmount() { diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index 31457db54..ee4d19a3d 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -270,6 +270,22 @@ public class LinearWaypointProvider implements EnumerableWaypointProvider { this.markers = new EntityMarkers(); } + private void addWaypoint(Location at) { + Waypoint element = new Waypoint(at); + int idx = waypoints.size(); + if (waypoints.indexOf(selectedWaypoint) != -1) { + idx = waypoints.indexOf(selectedWaypoint); + waypoints.add(idx, element); + } else { + waypoints.add(element); + } + + if (showingMarkers) { + markers.createMarker(element, element.getLocation().clone()); + } + Messaging.sendTr(player, Messages.LINEAR_WAYPOINT_EDITOR_ADDED_WAYPOINT, formatLoc(at), waypoints.size()); + } + @Override public void begin() { Messaging.sendTr(player, Messages.LINEAR_WAYPOINT_EDITOR_BEGIN); @@ -369,6 +385,10 @@ public class LinearWaypointProvider implements EnumerableWaypointProvider { Messaging.sendTr(event.getPlayer(), cycle ? Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_SET : Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_UNSET); }); + } else if (message.equalsIgnoreCase("here")) { + event.setCancelled(true); + Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + () -> addWaypoint(player.getLocation())); } } @@ -394,20 +414,7 @@ public class LinearWaypointProvider implements EnumerableWaypointProvider { } } - Waypoint element = new Waypoint(at); - int idx = waypoints.size(); - if (waypoints.indexOf(selectedWaypoint) != -1) { - idx = waypoints.indexOf(selectedWaypoint); - waypoints.add(idx, element); - } else { - waypoints.add(element); - } - - if (showingMarkers) { - markers.createMarker(element, element.getLocation().clone()); - } - Messaging.sendTr(player, Messages.LINEAR_WAYPOINT_EDITOR_ADDED_WAYPOINT, formatLoc(at), - waypoints.size()); + addWaypoint(at); } else if (waypoints.size() > 0 && !event.getPlayer().isSneaking()) { event.setCancelled(true); diff --git a/main/src/main/java/net/citizensnpcs/util/NMS.java b/main/src/main/java/net/citizensnpcs/util/NMS.java index a4891d984..c3e0183ce 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMS.java +++ b/main/src/main/java/net/citizensnpcs/util/NMS.java @@ -868,6 +868,10 @@ public class NMS { BRIDGE.updateInventoryTitle(player, view, newTitle); } + public static void updateMountedInteractionHeight(Entity entity, Entity mount, double height) { + BRIDGE.updateMountedInteractionHeight(entity, mount, height); + } + public static void updateNavigationWorld(org.bukkit.entity.Entity entity, org.bukkit.World world) { BRIDGE.updateNavigationWorld(entity, world); } @@ -879,7 +883,6 @@ public class NMS { private static Method ADD_OPENS; private static NMSBridge BRIDGE; - private static Method GET_MODULE; private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static Field MODIFIERS_FIELD; @@ -894,6 +897,7 @@ public class NMS { private static MethodHandle UNSAFE_PUT_LONG; private static MethodHandle UNSAFE_PUT_OBJECT; private static MethodHandle UNSAFE_STATIC_FIELD_OFFSET; + static { try { Class.forName("com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent"); diff --git a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java index d2c6186d0..b5f69fc40 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java +++ b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java @@ -260,7 +260,10 @@ public interface NMSBridge { public void updateInventoryTitle(Player player, InventoryView view, String newTitle); + public default void updateMountedInteractionHeight(Entity entity, Entity mount, double height) { + } + public void updateNavigationWorld(Entity entity, World world); - public void updatePathfindingRange(NPC npc, float pathfindingRange); + public void updatePathfindingRange(NPC npc, float pathfindingRange);; } diff --git a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java index d157c2294..446451368 100644 --- a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java +++ b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java @@ -280,10 +280,12 @@ import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; import net.minecraft.network.protocol.game.VecDeltaCodec; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerAdvancements; @@ -304,6 +306,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity.RemovalReason; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Interaction; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; @@ -1795,6 +1798,22 @@ public class NMSImpl implements NMSBridge { player.updateInventory(); } + @Override + public void updateMountedInteractionHeight(org.bukkit.entity.Entity entity, org.bukkit.entity.Entity mount, + double offset) { + Interaction handle = (Interaction) getHandle(entity); + offset += -0.5 + getHandle(mount).getPassengersRidingOffset(); + ((org.bukkit.entity.Interaction) entity).setInteractionHeight((float) offset); + mount.addPassenger(entity); + handle.setPose(Pose.SNIFFING); + Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> { + if (!entity.isValid()) + return; + sendPacketNearby(null, entity.getLocation(), new ClientboundSetEntityDataPacket(handle.getId(), + List.of(new SynchedEntityData.DataItem<>(INTERACTION_HEIGHT, 999999f).value()))); + }); + } + @Override public void updateNavigationWorld(org.bukkit.entity.Entity entity, World world) { if (NAVIGATION_WORLD_FIELD == null) @@ -2547,6 +2566,7 @@ public class NMSImpl implements NMSBridge { private static final MethodHandle HEAD_HEIGHT = NMS.getSetter(Entity.class, "bf"); private static final MethodHandle HEAD_HEIGHT_METHOD = NMS.getFirstMethodHandle(Entity.class, true, Pose.class, EntityDimensions.class); + private static EntityDataAccessor INTERACTION_HEIGHT = null; private static final MethodHandle JUMP_FIELD = NMS.getGetter(LivingEntity.class, "bi"); private static final MethodHandle LOOK_CONTROL_SETTER = NMS.getFirstSetter(Mob.class, LookControl.class); private static final MethodHandle MAKE_REQUEST = NMS.getMethodHandle(YggdrasilAuthenticationService.class, @@ -2602,5 +2622,10 @@ public class NMSImpl implements NMSBridge { } catch (Throwable e) { e.printStackTrace(); } + try { + INTERACTION_HEIGHT = (EntityDataAccessor) NMS.getGetter(Interaction.class, "d").invoke(); + } catch (Throwable e) { + e.printStackTrace(); + } } } diff --git a/v1_20_R1/src/main/java/net/citizensnpcs/nms/v1_20_R1/util/NMSImpl.java b/v1_20_R1/src/main/java/net/citizensnpcs/nms/v1_20_R1/util/NMSImpl.java index c7e550468..fac14665f 100644 --- a/v1_20_R1/src/main/java/net/citizensnpcs/nms/v1_20_R1/util/NMSImpl.java +++ b/v1_20_R1/src/main/java/net/citizensnpcs/nms/v1_20_R1/util/NMSImpl.java @@ -279,10 +279,12 @@ import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; import net.minecraft.network.protocol.game.VecDeltaCodec; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerAdvancements; @@ -303,6 +305,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity.RemovalReason; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Interaction; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; @@ -395,7 +398,7 @@ public class NMSImpl implements NMSBridge { e.printStackTrace(); } return success; - } + }; @Override public void addOrRemoveFromPlayerList(org.bukkit.entity.Entity entity, boolean remove) { @@ -1795,6 +1798,22 @@ public class NMSImpl implements NMSBridge { player.updateInventory(); } + @Override + public void updateMountedInteractionHeight(org.bukkit.entity.Entity entity, org.bukkit.entity.Entity mount, + double offset) { + Interaction handle = (Interaction) getHandle(entity); + offset += -0.5 + getHandle(mount).getPassengersRidingOffset(); + ((org.bukkit.entity.Interaction) entity).setInteractionHeight((float) offset); + mount.addPassenger(entity); + handle.setPose(Pose.SNIFFING); + Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> { + if (!entity.isValid()) + return; + sendPacketNearby(null, entity.getLocation(), new ClientboundSetEntityDataPacket(handle.getId(), + List.of(new SynchedEntityData.DataItem<>(INTERACTION_HEIGHT, 999999f).value()))); + }); + } + @Override public void updateNavigationWorld(org.bukkit.entity.Entity entity, World world) { if (NAVIGATION_WORLD_FIELD == null) @@ -2509,6 +2528,7 @@ public class NMSImpl implements NMSBridge { private static final MethodHandle ADVANCEMENTS_PLAYER_SETTER = NMS.getFirstFinalSetter(ServerPlayer.class, PlayerAdvancements.class); + private static final MethodHandle ATTRIBUTE_PROVIDER_MAP = NMS.getFirstGetter(AttributeSupplier.class, Map.class); private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFirstFinalSetter(AttributeSupplier.class, Map.class); @@ -2548,6 +2568,7 @@ public class NMSImpl implements NMSBridge { private static final MethodHandle HEAD_HEIGHT = NMS.getSetter(Entity.class, "bi"); private static final MethodHandle HEAD_HEIGHT_METHOD = NMS.getFirstMethodHandle(Entity.class, true, Pose.class, EntityDimensions.class); + private static EntityDataAccessor INTERACTION_HEIGHT = null; private static final MethodHandle JUMP_FIELD = NMS.getGetter(LivingEntity.class, "bk"); private static final MethodHandle LOOK_CONTROL_SETTER = NMS.getFirstSetter(Mob.class, LookControl.class); private static final MethodHandle MAKE_REQUEST = NMS.getMethodHandle(YggdrasilAuthenticationService.class, @@ -2603,5 +2624,10 @@ public class NMSImpl implements NMSBridge { } catch (Throwable e) { e.printStackTrace(); } + try { + INTERACTION_HEIGHT = (EntityDataAccessor) NMS.getGetter(Interaction.class, "d").invoke(); + } catch (Throwable e) { + e.printStackTrace(); + } } }