diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index c72deaa84..e33be5b62 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -106,6 +106,7 @@ import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.editor.Editor; import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.npc.skin.SkinUpdateTracker; +import net.citizensnpcs.npc.skin.SkinnableEntity; import net.citizensnpcs.trait.ClickRedirectTrait; import net.citizensnpcs.trait.CommandTrait; import net.citizensnpcs.trait.Controllable; @@ -462,6 +463,12 @@ public class EventListen implements Listener { @EventHandler(ignoreCancelled = true) public void onNPCLinkToPlayer(NPCLinkToPlayerEvent event) { NPC npc = event.getNPC(); + if (npc.getEntity() instanceof SkinnableEntity) { + SkinnableEntity skinnable = (SkinnableEntity) npc.getEntity(); + if (skinnable.getSkinTracker().getSkin() != null) { + skinnable.getSkinTracker().getSkin().apply(skinnable); + } + } if (npc.isSpawned() && npc.getEntity().getType() == EntityType.PLAYER) { onNPCPlayerLinkToPlayer(event); } @@ -483,7 +490,6 @@ public class EventListen implements Listener { NMS.sendPositionUpdateNearby(tracker, false, null, null, NMS.getHeadYaw(tracker)); }, Setting.TABLIST_REMOVE_PACKET_DELAY.asTicks() + 1); - boolean resetYaw = event.getNPC().data().get(NPC.Metadata.RESET_YAW_ON_SPAWN, Setting.RESET_YAW_ON_SPAWN.asBoolean()); boolean sendTabRemove = NMS.sendTabListAdd(event.getPlayer(), (Player) tracker); diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java index a4d2d0620..d8946cdc7 100644 --- a/main/src/main/java/net/citizensnpcs/Settings.java +++ b/main/src/main/java/net/citizensnpcs/Settings.java @@ -274,10 +274,10 @@ public class Settings { USE_SCOREBOARD_TEAMS("npc.scoreboard-teams.enable", true), WARN_ON_RELOAD("general.reload-warning-enabled", true),; - protected String comments; + private String comments; private Duration duration; - protected String migrate; - protected String path; + private String migrate; + private final String path; protected Object value; Setting(String path, Object value) { @@ -347,14 +347,7 @@ public class Settings { } protected void loadFromKey(DataKey root) { - if (SUPPORTS_SET_COMMENTS && root.keyExists(path)) { - try { - ((YamlKey) root).getSection("").setComments(path, - comments == null ? null : Arrays.asList(comments.split("
"))); - } catch (Throwable t) { - SUPPORTS_SET_COMMENTS = false; - } - } + setComments(root); if (migrate != null && root.keyExists(migrate) && !root.keyExists(path)) { value = root.getRaw(migrate); root.removeKey(migrate); @@ -365,6 +358,18 @@ public class Settings { protected void setAtKey(DataKey root) { root.setRaw(path, value); + setComments(root); + } + + private void setComments(DataKey root) { + if (SUPPORTS_SET_COMMENTS && root.keyExists(path)) { + try { + ((YamlKey) root).getSection("").setComments(path, + comments == null ? null : Arrays.asList(comments.split("
"))); + } catch (Throwable t) { + SUPPORTS_SET_COMMENTS = false; + } + } } } diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index a9c34bea8..97d5906a9 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -339,7 +339,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "armorstand --visible [visible] --small [small] --marker [marker] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", + usage = "armorstand --visible [visible] --small [small] --marker [marker] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(head|body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", desc = "Edit armorstand properties", modifiers = { "armorstand" }, min = 1, @@ -370,6 +370,9 @@ public class NPCCommands { trait.setHasBaseplate(baseplate); } ArmorStand ent = (ArmorStand) npc.getEntity(); + if (args.hasValueFlag("headpose")) { + ent.setHeadPose(args.parseEulerAngle(args.getFlag("headpose"))); + } if (args.hasValueFlag("bodypose")) { ent.setBodyPose(args.parseEulerAngle(args.getFlag("bodypose"))); } diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index 9aac7aa3b..29343e64f 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -306,7 +306,20 @@ public class CitizensNPC extends AbstractNPC { entityController.create(at.clone(), this); getEntity().setMetadata("NPC", new FixedMetadataValue(CitizensAPI.getPlugin(), true)); getEntity().setMetadata("NPC-ID", new FixedMetadataValue(CitizensAPI.getPlugin(), getId())); - + // Spawning the entity will create an entity tracker that is not controlled by Citizens. This is fixed later in + // spawning; to avoid sending packets twice, try to hide the entity initially + if (SUPPORT_VISIBLE_BY_DEFAULT) { + try { + getEntity().setVisibleByDefault(false); + } catch (NoSuchMethodError err) { + SUPPORT_VISIBLE_BY_DEFAULT = false; + } + } + if (!SUPPORT_VISIBLE_BY_DEFAULT && getEntity().getType() == EntityType.PLAYER) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + player.hidePlayer((Player) getEntity()); + } + } if (getEntity() instanceof SkinnableEntity && !hasTrait(SkinLayers.class)) { ((SkinnableEntity) getEntity()).setSkinFlags(EnumSet.allOf(SkinLayers.Layer.class)); } @@ -331,15 +344,12 @@ public class CitizensNPC extends AbstractNPC { Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(this, at)); return false; } - // send skin packets, if applicable, before other NMS packets are sent - SkinnableEntity skinnable = getEntity() instanceof SkinnableEntity ? (SkinnableEntity) getEntity() : null; - if (skinnable != null) { - skinnable.getSkinTracker().onSpawnNPC(); - } NMS.setLocationDirectly(getEntity(), at); NMS.setHeadYaw(getEntity(), at.getYaw()); NMS.setBodyYaw(getEntity(), at.getYaw()); + // Paper now doesn't actually set entities as valid for a few ticks while adding entities to chunks + // Need to check the entity is really valid for a few ticks before finalising spawning Location to = at; Consumer postSpawn = new Consumer() { private int timer; @@ -371,7 +381,6 @@ public class CitizensNPC extends AbstractNPC { return; } navigator.onSpawn(); - for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) { try { trait.onSpawn(); @@ -380,9 +389,16 @@ public class CitizensNPC extends AbstractNPC { ex.printStackTrace(); } } - EntityType type = getEntity().getType(); + // Replace the entity tracker and attempt to show the entity NMS.replaceTracker(getEntity()); - + if (SUPPORT_VISIBLE_BY_DEFAULT) { + getEntity().setVisibleByDefault(true); + } else if (getEntity().getType() == EntityType.PLAYER) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + player.showPlayer((Player) getEntity()); + } + } + EntityType type = getEntity().getType(); if (type.isAlive()) { LivingEntity entity = (LivingEntity) getEntity(); entity.setRemoveWhenFarAway(false); @@ -617,4 +633,5 @@ public class CitizensNPC extends AbstractNPC { private static boolean SUPPORT_PICKUP_ITEMS = true; private static boolean SUPPORT_SILENT = true; private static boolean SUPPORT_USE_ITEM = true; + private static boolean SUPPORT_VISIBLE_BY_DEFAULT = true; } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java index 5129560d2..764f3b456 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java @@ -99,9 +99,9 @@ public class Skin { && !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin()) return false; - if (hasFetched) + if (hasFetched) { return true; - else { + } else { if (!fetching) { fetch(); } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java index 06412cad2..7b31a879f 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java @@ -191,7 +191,7 @@ public class SkinPacketTracker { inProgress.put(player.getUniqueId(), entry); skin.apply(entity); if (NMS.sendTabListAdd(player, entity.getBukkitEntity())) { - scheduleRemovePacket(entry, 2); + scheduleRemovePacket(entry, Setting.TABLIST_REMOVE_PACKET_DELAY.asTicks()); } } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java index bb5775928..e7dcdb90c 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java @@ -279,7 +279,7 @@ public class SkinUpdateTracker { } /** - * Update a player with skin related packets from nearby skinnable NPC's. + * Update a player with skin related packets from nearby skinnable NPCs. * * @param player * The player to update.