ProtocolManager.updateEntity method doesn't work in Spigot 1.17.1
Lori3f6 opened this issue · 6 comments
Describe the bug
Hello, I am using ProtocolLib to make a entity vanish from a player's client, so I send a ENTITY_DESTROY packet to a player. It works properly. Then I am going to use updateEntity(org.bukkit.entity.Entity entity, List<org.bukkit.entity.Player> observers)
method to make the entity appear on the player client again, but it changes nothing on client.
To Reproduce
I made a plug-in and run the following code. These code are expected to make all LivingEntities except Player vanishing and presenting in player's client alternatively.
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class TwinkleEntities extends JavaPlugin {
@Override
public void onEnable() {
var protocolManager = ProtocolLibrary.getProtocolManager();
AtomicBoolean flag = new AtomicBoolean(false);
getServer().getScheduler().runTaskTimer(this, () -> {
if (getServer().getOnlinePlayers().isEmpty())
return;
var player = (Player) getServer().getOnlinePlayers().toArray()[0];
if (flag.get()) {
var vanishPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
var entityIDList = new ArrayList<Integer>();
for (var entity : player.getWorld().getLivingEntities()) {
if (!(entity instanceof Player)) {
entityIDList.add(entity.getEntityId());
}
}
vanishPacket.getIntLists().write(
0, entityIDList
);
try {
protocolManager.sendServerPacket(player, vanishPacket);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
player.sendMessage("Entities vanished!");
} else {
for (var entity : player.getWorld().getLivingEntities()) {
if (!(entity instanceof Player)) {
protocolManager.updateEntity(entity, List.of(player));
}
}
player.sendMessage("Entities shown!");
}
flag.set(!flag.get());
}, 0L, 20L);
}
}
Expected behavior
The entity around the player twinkles between appearance and disappearance every second. But entities don't present again after vanished.
Screenshots
This gif may shorternd by github automatically, you could see a longer version here if you want: https://global.cdn.blingwang.cn/2021/10/17/9233d3d691502.gif
Version Info
https://pastebin.mozilla.org/asyh7mqC/raw
Additional context
I don't know why the entity don't presents after disappearing. If the problem is caused by incorrect API call in my code, please don't hesitate to correct me. Thanks a lot.
Empty below.
I have also duplicated this problem with effectively the same code.
updateEntity() on 1.16 will un-hide an entity that is destroyed, 1.17 with exact same code no longer shows the entity.
public final boolean showEntity(Player observer, Entity entity) {
try {
validate(observer, entity);
boolean hiddenBefore = !setVisibility(observer, entity.getEntityId(), true);
//observer.sendMessage("Shown " + entity.getType() + " Was hidden? " + hiddenBefore);
// Resend packets
if (manager != null && hiddenBefore) {
List<Player> thing = Arrays.asList(observer);
manager.updateEntity(entity, thing);
for (int i = 1; i <= 20; i++) { //Attempt to brute-force the issue to see if that helped, it didnt :D
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TaskHandler.javaPluginClass, new Runnable() {
@Override
public void run() {
manager.updateEntity(entity, thing);
}
}, i);
}
}
return hiddenBefore;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
I have done some testing, and you have to send a SPAWN_ENTITY_LIVING packet like this:
` //Note: It is literally midnight for me and I am going to bed, all messy bits of code have been left in
PacketContainer newPacket = new PacketContainer(SPAWN_ENTITY_LIVING);
newPacket.getIntegers().
write(0, entity.getEntityId()).
write(1, (int) 71). //MAKE ALL ENTITIES RABBITS. I AM THE RABBIT LORD. (Also too lazy to work out how to get correct entity IDs right now)
write(2, -106).
write(3, 338).
write(4, 514);
newPacket.getDoubles().
write(0, (double) entity.getLocation().getX() * 1).
write(1, (double) entity.getLocation().getY() * 1).
write(2, (double) entity.getLocation().getZ() * 1);
//write(3, (double) entity.getLocation().getPitch()).
// write(4, (double) entity.getLocation().getYaw());
newPacket.getUUIDs().write(0, entity.getUniqueId());
/*newPacket.getShorts().
write(0, (short) 0).
write(1, (short) 0).
write(2, (short) 0);*/
//newPacket.getFloat().write(0, entity.getLocation().getPitch());
// newPacket.getFloat().write(1, entity.getLocation().getYaw());
try {
ProtocolLibrary.getProtocolManager().sendServerPacket(observer, newPacket);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}`
Doing this with literally the same entity ID of the entity that was hidden will make the entity appear again to the client.
Hey,
sorry for the late response on this issue; thanks for the report! As we're changing quite a lot of code in v5.0 (see #1524 for now) I will keep this issue in mind and take a look into it when testing the new version. Might be that the issue is already resolved, will see.
I have done some testing, and you have to send a SPAWN_ENTITY_LIVING packet like this:
` //Note: It is literally midnight for me and I am going to bed, all messy bits of code have been left in PacketContainer newPacket = new PacketContainer(SPAWN_ENTITY_LIVING); newPacket.getIntegers(). write(0, entity.getEntityId()). write(1, (int) 71). //MAKE ALL ENTITIES RABBITS. I AM THE RABBIT LORD. (Also too lazy to work out how to get correct entity IDs right now) write(2, -106). write(3, 338). write(4, 514); newPacket.getDoubles(). write(0, (double) entity.getLocation().getX() * 1). write(1, (double) entity.getLocation().getY() * 1). write(2, (double) entity.getLocation().getZ() * 1); //write(3, (double) entity.getLocation().getPitch()). // write(4, (double) entity.getLocation().getYaw()); newPacket.getUUIDs().write(0, entity.getUniqueId()); /*newPacket.getShorts(). write(0, (short) 0). write(1, (short) 0). write(2, (short) 0);*/ //newPacket.getFloat().write(0, entity.getLocation().getPitch()); // newPacket.getFloat().write(1, entity.getLocation().getYaw()); try { ProtocolLibrary.getProtocolManager().sendServerPacket(observer, newPacket); } catch (InvocationTargetException e) { e.printStackTrace(); } }`
Doing this with literally the same entity ID of the entity that was hidden will make the entity appear again to the client.
Note that players, paintings, exp orbs and other non-living entities use different packet types, so they require different handling.