Paper
                                
                                
                                
                                    Paper copied to clipboard
                            
                            
                            
                        Player#setSpectatorTarget does not "attach" the spectator to it's target
Expected behavior
Using Player#setSpectatorTarget to target a player would attach the spectator's camera to the player in the same way that the spectator clicking on the player from their client would.
Observed/Actual behavior
Using Player#setSpectatorTarget to target a player teleports the spectator to the target player's location and then will teleport them again when the target player gets too far away. The spectator player isn't following the target's player camera and isn't "attached" to the player.
Steps/models to reproduce
I made a basic plugin that just runs the following code when the target player joins, which uses the Player#setSpectatorTarget method on the spectatorPlayer to target the targetPlayer.
class PlayerJoinEventListener implements Listener {
    UUID spectatorPlayer = UUID.fromString("04fbb67a-02ef-437b-be73-c04fab4f3b43");
    UUID targetPlayer = UUID.fromString("bf8b08a5-714c-4667-8f49-efce56cb7dc5");
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event){
        if(event.getPlayer().getUniqueId().equals(targetPlayer)){
            Bukkit.getServer().getPlayer(spectatorPlayer).setSpectatorTarget(event.getPlayer());
        }
    }
}
Attached is a video of this code running with the spectatorPlayer starting in the exact same position that the targetPlayer joins into because of a previously failed spectate.
https://github.com/PaperMC/Paper/assets/16448318/f2fd9157-dab3-40de-a37c-50bc2eebcfe8
Plugin and Datapack List
Only the above mentioned test plugin
Paper version
This server is running Paper version git-Paper-423 (MC: 1.20.4) (Implementing API version 1.20.4-R0.1-SNAPSHOT) (Git: 31699ae) You are running the latest version
Other
No response
I can only reproduce this directly in the PlayerJoinEvent, waiting one tick solves the problem. I suspect this is because the join event is too early and existing players have not been notified of the new player having joined (which happens a bit after the join event, until then it simply does not exist to other players), and it's the server forcing the spectator's position. Waiting one tick you ensure the spectator is told about the entity you want them to spectate and will follow them properly on the client
Thank you! That makes a lot of sense, however I was able to get it working locally with a delay of 4 ticks, but when using that delay on a server with a higher ping that delay doesn't deem to be enough. Is there a way for me to tell if the spectator is attached or has been sent the entity?
After testing around for some time it seems that latency is not of the matter here, delaying the action by one tick seems to always work fine when both players are within range; there is however this vanilla issue that #9350 does fix, but the issue with calling setSpectatorTarget on join has nothing to do with that, that area is pretty much WAI.
Got it. I was able to fix both by periodically checking if the spectator is both within 3 blocks of and in the same dimension as the target player, but I was never able to get it to work with as short of a delay as 1 tick, so have stuck with 4 on the join event with the occasional need for the check to fix it.
It may not be ping related and instead be something to do with when/how it's being called in the actual plugin, but it's at least working and has a check for when it breaks.