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.