Attempt to replace the entity tracker without sending packets for initial spawn. EXPERIMENTAL: needs testing. Also add /npc armorstand --headpose

This commit is contained in:
fullwall 2024-02-18 00:44:38 +08:00
parent 4d1b3ee357
commit 19f08c4607
7 changed files with 57 additions and 26 deletions

View File

@ -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);

View File

@ -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("<br>")));
} 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("<br>")));
} catch (Throwable t) {
SUPPORTS_SET_COMMENTS = false;
}
}
}
}

View File

@ -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")));
}

View File

@ -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<Runnable> postSpawn = new Consumer<Runnable>() {
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;
}

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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.