diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java
index 435b192bf..ba2e3c93e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java
@@ -15,7 +15,6 @@ public class BedrockPlayerInitializedTranslator extends PacketTranslator<SetLoca
 
                 for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
                     if (!entity.isValid()) {
-                        entity.sendPlayer(session);
                         // async skin loading
                         SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
                     }
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
index 4d00bdcbc..660cac2ec 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
@@ -51,8 +51,9 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
         entity.setPosition(position);
         entity.setRotation(rotation);
 
-        entity.sendPlayer(session);
         // async skin loading
-        SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
+        if (session.getUpstream().isInitialized()) {
+            SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
+        }
     }
 }
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkin.java b/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkin.java
index 71bf29845..78cd8933d 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkin.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkin.java
@@ -3,7 +3,6 @@ package org.geysermc.connector.utils;
 import lombok.Getter;
 
 import javax.imageio.ImageIO;
-import java.awt.Color;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -19,11 +18,11 @@ public class ProvidedSkin {
             try {
                 for (int y = 0; y < image.getHeight(); y++) {
                     for (int x = 0; x < image.getWidth(); x++) {
-                        Color color = new Color(image.getRGB(x, y), true);
-                        outputStream.write(color.getRed());
-                        outputStream.write(color.getGreen());
-                        outputStream.write(color.getBlue());
-                        outputStream.write(color.getAlpha());
+                        int rgba = image.getRGB(x, y);
+                        outputStream.write((rgba >> 16) & 0xFF); // Red
+                        outputStream.write((rgba >> 8) & 0xFF); // Green
+                        outputStream.write(rgba & 0xFF); // Blue
+                        outputStream.write((rgba >> 24) & 0xFF); // Alpha
                     }
                 }
                 image.flush();
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 2d2aceabf..b2634e3be 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
@@ -22,12 +22,12 @@ public class SkinProvider {
     public static final boolean ALLOW_THIRD_PARTY_CAPES = ((GeyserConnector)Geyser.getConnector()).getConfig().isAllowThirdPartyCapes();
     private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14);
 
-    public static final Skin EMPTY_SKIN = new Skin(-1, "");
     public static final byte[] STEVE_SKIN = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin();
+    public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN);
     private static Map<UUID, Skin> cachedSkins = new ConcurrentHashMap<>();
     private static Map<UUID, CompletableFuture<Skin>> requestedSkins = new ConcurrentHashMap<>();
 
-    public static final Cape EMPTY_CAPE = new Cape("", new byte[0], -1, true);
+    public static final Cape EMPTY_CAPE = new Cape("", "no-cape", new byte[0], -1, true);
     private static Map<String, Cape> cachedCapes = new ConcurrentHashMap<>();
     private static Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
 
@@ -149,8 +149,11 @@ public class SkinProvider {
             cape = requestImage(capeUrl, true);
         } catch (Exception ignored) {} // just ignore I guess
 
+        String[] urlSection = capeUrl.split("/"); // A real url is expected at this stage
+
         return new Cape(
                 capeUrl,
+                urlSection[urlSection.length - 1], // get the texture id and use it as cape id
                 cape.length > 0 ? cape : EMPTY_CAPE.getCapeData(),
                 System.currentTimeMillis(),
                 cape.length == 0
@@ -209,13 +212,14 @@ public class SkinProvider {
     public static class Skin {
         private UUID skinOwner;
         private String textureUrl;
-        private byte[] skinData = STEVE_SKIN;
+        private byte[] skinData;
         private long requestedOn;
         private boolean updated;
 
-        private Skin(long requestedOn, String textureUrl) {
+        private Skin(long requestedOn, String textureUrl, byte[] skinData) {
             this.requestedOn = requestedOn;
             this.textureUrl = textureUrl;
+            this.skinData = skinData;
         }
     }
 
@@ -223,6 +227,7 @@ public class SkinProvider {
     @Getter
     public static class Cape {
         private String textureUrl;
+        private String capeId;
         private byte[] capeData;
         private long requestedOn;
         private boolean failed;
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 988501b8b..640ae4500 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
@@ -20,6 +20,7 @@ import java.util.function.Consumer;
 public class SkinUtils {
     public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) {
         GameProfileData data = GameProfileData.from(profile);
+        SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
 
         return buildEntryManually(
                 profile.getId(),
@@ -27,7 +28,8 @@ public class SkinUtils {
                 geyserId,
                 profile.getIdAsString(),
                 SkinProvider.getCachedSkin(profile.getId()).getSkinData(),
-                SkinProvider.getCachedCape(data.getCapeUrl()).getCapeData(),
+                cape.getCapeId(),
+                cape.getCapeData(),
                 getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
                 ""
         );
@@ -40,6 +42,7 @@ public class SkinUtils {
                 geyserId,
                 profile.getIdAsString(),
                 SkinProvider.STEVE_SKIN,
+                SkinProvider.EMPTY_CAPE.getCapeId(),
                 SkinProvider.EMPTY_CAPE.getCapeData(),
                 getLegacySkinGeometry("geometry.humanoid"),
                 ""
@@ -47,18 +50,13 @@ public class SkinUtils {
     }
 
     public static PlayerListPacket.Entry buildEntryManually(UUID uuid, String username, long geyserId,
-                                                            String skinId, byte[] skinData, byte[] capeData,
+                                                            String skinId, byte[] skinData,
+                                                            String capeId, byte[] capeData,
                                                             String geometryName, String geometryData) {
-        if (skinData == null || skinData.length == 0) {
-            skinData = SkinProvider.EMPTY_SKIN.getSkinData();
-        }
-
-        if (capeData == null || capeData.length == 0) {
-            capeData = SkinProvider.EMPTY_CAPE.getCapeData();
-        }
-
-        SerializedSkin serializedSkin = SerializedSkin.of(skinId, geometryName, ImageData.of(64, 64, skinData),
-                Collections.emptyList(), ImageData.of(64, 32, capeData), geometryData, "", true, false, false, "", "");
+        SerializedSkin serializedSkin = SerializedSkin.of(
+                skinId, geometryName, ImageData.of(skinData), Collections.emptyList(),
+                ImageData.of(capeData), geometryData, "", true, false, false, capeId, uuid.toString()
+        );
 
         PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
         entry.setName(username);
@@ -107,7 +105,7 @@ public class SkinUtils {
     public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session,
                                                    Consumer<SkinProvider.SkinAndCape> skinAndCapeConsumer) {
         Geyser.getGeneralThreadPool().execute(() -> {
-            SkinUtils.GameProfileData data = SkinUtils.GameProfileData.from(entity.getProfile());
+            GameProfileData data = GameProfileData.from(entity.getProfile());
 
             SkinProvider.requestSkinAndCape(entity.getUuid(), data.getSkinUrl(), data.getCapeUrl())
                     .whenCompleteAsync((skinAndCape, throwable) -> {
@@ -126,12 +124,13 @@ public class SkinUtils {
                                 entity.setLastSkinUpdate(skin.getRequestedOn());
 
                                 if (session.getUpstream().isInitialized()) {
-                                    PlayerListPacket.Entry updatedEntry = SkinUtils.buildEntryManually(
+                                    PlayerListPacket.Entry updatedEntry = buildEntryManually(
                                             entity.getUuid(),
                                             entity.getUsername(),
                                             entity.getGeyserId(),
                                             entity.getUuid().toString(),
                                             skin.getSkinData(),
+                                            cape.getCapeId(),
                                             cape.getCapeData(),
                                             getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
                                             ""