diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java index 7a48f553..bff64bec 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -141,6 +141,22 @@ public class REntityServer implements Listener { } } + /** + * Re-sends list and spawn packets of an already added entity to all current viewers, + * e.g. when information that is part of the tab list entry changed after the entity was added. + */ + void refreshEntity(REntity entity) { + if (!entityMap.containsKey(entity.entityId)) return; + + for (Player player : players.getOrDefault(entityToId(entity), emptyPlayers)) { + Consumer sink = packet -> TinyProtocol.instance.sendPacket(player, packet); + if (!entity.isHidden()) entity.despawn(sink); + entity.delist(sink); + entity.list(sink); + if (!entity.isHidden()) entity.spawn(sink); + } + } + void removeEntity(REntity entity) { entity.despawn(packet -> updateEntity(entity, packet)); removeEntityFromChunk(entity); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index cfb6304d..b57e1ee3 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -76,10 +76,20 @@ public class RPlayer extends REntity implements RInteractableEntity { } private GameProfile saved; + private boolean awaitingSkin = false; @Override void list(Consumer packetSink) { saved = getGameProfile(); + if (saved.getProperties().isEmpty() && !awaitingSkin) { + // The skin is requested asynchronously, so it is usually not available yet when the entity is created. + // Without a refresh, players that already see this entity would keep the default skin. + awaitingSkin = true; + CoreNetworkHandler.onSkinReceived(actualUUID, () -> { + awaitingSkin = false; + server.refreshEntity(this); + }); + } packetSink.accept(ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, saved, GameMode.CREATIVE)); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java index bc5d471e..a5fe8cf5 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java @@ -21,6 +21,7 @@ package de.steamwar.network; import com.mojang.authlib.properties.Property; import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Core; import de.steamwar.network.handlers.InventoryHandler; import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.common.PlayerSkinResponsePacket; @@ -31,8 +32,11 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; public class CoreNetworkHandler extends PacketHandler { @@ -85,9 +89,23 @@ public class CoreNetworkHandler extends PacketHandler { } }; + private static final Map> SKIN_LISTENERS = new ConcurrentHashMap<>(); + + /** + * Runs the given callback on the main thread once the skin of the given player has been received. + */ + public static void onSkinReceived(UUID uuid, Runnable callback) { + SKIN_LISTENERS.computeIfAbsent(uuid, __ -> new CopyOnWriteArrayList<>()).add(callback); + } + @Handler public void handlePlayerSkinResponse(PlayerSkinResponsePacket packet) { if (!SKIN_DATA_PROMISES.containsKey(packet.getUuid())) return; SKIN_DATA_PROMISES.put(packet.getUuid(), new Property("textures", packet.getSkin(), packet.getSignature())); + + Bukkit.getScheduler().runTask(Core.getInstance(), () -> { + List listeners = SKIN_LISTENERS.remove(packet.getUuid()); + if (listeners != null) listeners.forEach(Runnable::run); + }); } }