diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java index 87da2d00cb3..02cf01c5ac0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java @@ -43,10 +43,8 @@ public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession se session.login(); for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) { - if (!entity.isValid()) { - // async skin loading - SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session)); - } + // async skin loading + SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session)); } } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index aea9ba1892b..cdf02c45a2a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -53,7 +53,7 @@ public class SkinProvider { public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN); public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin(); public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN); - private static Map cachedSkins = new ConcurrentHashMap<>(); + private static Map cachedSkins = new ConcurrentHashMap<>(); private static Map> requestedSkins = new ConcurrentHashMap<>(); public static final Cape EMPTY_CAPE = new Cape("", "no-cape", new byte[0], -1, true); @@ -68,7 +68,7 @@ public class SkinProvider { public static String EARS_GEOMETRY_SLIM; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes + private static final int CACHE_INTERVAL = 30 * 60 * 1000; // 30 minutes static { /* Load in the normal ears geometry */ @@ -103,10 +103,6 @@ public class SkinProvider { EARS_GEOMETRY_SLIM = earsDataBuilder.toString(); } - public static boolean hasSkinCached(UUID uuid) { - return cachedSkins.containsKey(uuid); - } - public static boolean hasCapeCached(String capeUrl) { return cachedCapes.containsKey(capeUrl); } @@ -138,18 +134,18 @@ public static CompletableFuture requestSkin(UUID playerId, String textureU if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN); if (requestedSkins.containsKey(playerId)) return requestedSkins.get(playerId); // already requested - if ((System.currentTimeMillis() - CACHE_INTERVAL) < cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getRequestedOn()) { + if ((System.currentTimeMillis() - CACHE_INTERVAL) < cachedSkins.getOrDefault(textureUrl, EMPTY_SKIN).getRequestedOn()) { // no need to update, still cached - return CompletableFuture.completedFuture(cachedSkins.get(playerId)); + return CompletableFuture.completedFuture(cachedSkins.get(textureUrl)); } CompletableFuture future; if (newThread) { future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE) .whenCompleteAsync((skin, throwable) -> { - if (!cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getTextureUrl().equals(textureUrl)) { + if (!cachedSkins.getOrDefault(textureUrl, EMPTY_SKIN).getTextureUrl().equals(textureUrl)) { skin.updated = true; - cachedSkins.put(playerId, skin); + cachedSkins.put(textureUrl, skin); } requestedSkins.remove(skin.getSkinOwner()); }); @@ -157,7 +153,7 @@ public static CompletableFuture requestSkin(UUID playerId, String textureU } else { Skin skin = supplySkin(playerId, textureUrl); future = CompletableFuture.completedFuture(skin); - cachedSkins.put(playerId, skin); + cachedSkins.put(textureUrl, skin); } return future; } @@ -255,7 +251,7 @@ public static CompletableFuture requestBedrockGeometry(SkinGeometr public static void storeBedrockSkin(UUID playerID, String skinID, byte[] skinData) { Skin skin = new Skin(playerID, skinID, skinData, System.currentTimeMillis(), true, false); - cachedSkins.put(playerID, skin); + cachedSkins.put(skin.getTextureUrl(), skin); } public static void storeBedrockCape(UUID playerID, byte[] capeData) { @@ -275,7 +271,7 @@ public static void storeBedrockGeometry(UUID playerID, byte[] geometryName, byte * @param skin The skin to cache */ public static void storeEarSkin(UUID playerID, Skin skin) { - cachedSkins.put(playerID, skin); + cachedSkins.put(skin.getTextureUrl(), skin); } /** diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 48e4c4c80da..c35fe2d5507 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import lombok.AllArgsConstructor; import lombok.Getter; @@ -88,7 +89,7 @@ public static PlayerListPacket.Entry buildEntryManually(UUID uuid, String userna String geometryName, String geometryData) { SerializedSkin serializedSkin = SerializedSkin.of( skinId, geometryName, ImageData.of(skinData), Collections.emptyList(), - ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, uuid.toString() + ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, skinId ); PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid); @@ -202,48 +203,54 @@ public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSessio } } - if (entity.getLastSkinUpdate() < skin.getRequestedOn()) { - entity.setLastSkinUpdate(skin.getRequestedOn()); - - if (session.getUpstream().isInitialized()) { - PlayerListPacket.Entry updatedEntry = buildEntryManually( - entity.getUuid(), - entity.getUsername(), - entity.getGeyserId(), - entity.getUuid().toString(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), - geometry.getGeometryName(), - geometry.getGeometryData() - ); - - // If it is our skin we replace the UUID with the authdata UUID - if (session.getPlayerEntity() == entity) { - // Copy the entry with our identity instead. - PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID()); - copy.setName(updatedEntry.getName()); - copy.setEntityId(updatedEntry.getEntityId()); - copy.setSkin(updatedEntry.getSkin()); - copy.setXuid(updatedEntry.getXuid()); - copy.setPlatformChatId(updatedEntry.getPlatformChatId()); - copy.setTeacher(updatedEntry.isTeacher()); - updatedEntry = copy; - } - - PlayerListPacket playerRemovePacket = new PlayerListPacket(); - playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE); - playerRemovePacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerRemovePacket); - - PlayerListPacket playerAddPacket = new PlayerListPacket(); - playerAddPacket.setAction(PlayerListPacket.Action.ADD); - playerAddPacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerAddPacket); - - if(entity.getUuid().equals(session.getPlayerEntity().getUuid())) { - session.fetchOurSkin(updatedEntry); - } + entity.setLastSkinUpdate(skin.getRequestedOn()); + + if (session.getUpstream().isInitialized()) { + PlayerListPacket.Entry updatedEntry = buildEntryManually( + entity.getUuid(), + entity.getUsername(), + entity.getGeyserId(), + skin.getTextureUrl(), + skin.getSkinData(), + cape.getCapeId(), + cape.getCapeData(), + geometry.getGeometryName(), + geometry.getGeometryData() + ); + + // If it is our skin we replace the UUID with the authdata UUID + if (session.getPlayerEntity() == entity) { + // Copy the entry with our identity instead. + PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID()); + copy.setName(updatedEntry.getName()); + copy.setEntityId(updatedEntry.getEntityId()); + copy.setSkin(updatedEntry.getSkin()); + copy.setXuid(updatedEntry.getXuid()); + copy.setPlatformChatId(updatedEntry.getPlatformChatId()); + copy.setTeacher(updatedEntry.isTeacher()); + updatedEntry = copy; + } + + PlayerListPacket playerRemovePacket = new PlayerListPacket(); + playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE); + playerRemovePacket.getEntries().add(updatedEntry); + session.sendUpstreamPacket(playerRemovePacket); + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setAction(PlayerListPacket.Action.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.sendUpstreamPacket(playerAddPacket); + + if(entity.getUuid().equals(session.getPlayerEntity().getUuid())) { + session.fetchOurSkin(updatedEntry); + } else { + PlayerSkinPacket playerSkinPacket = new PlayerSkinPacket(); + playerSkinPacket.setUuid(entity.getUuid()); + playerSkinPacket.setSkin(updatedEntry.getSkin()); + playerSkinPacket.setOldSkinName("OldName"); + playerSkinPacket.setNewSkinName(String.valueOf(updatedEntry.getSkin().hashCode())); + playerSkinPacket.setTrustedSkin(true); + session.getUpstream().sendPacket(playerSkinPacket); } } } catch (Exception e) {