diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index aa7848da233..cd665f72ea5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -43,6 +43,7 @@ import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.network.session.cache.EntityEffectCache; +import org.geysermc.connector.utils.SkinProvider; import org.geysermc.connector.utils.SkinUtils; import java.util.ArrayList; @@ -60,6 +61,8 @@ public class PlayerEntity extends LivingEntity { private boolean onGround; private final EntityEffectCache effectCache; + private SkinProvider.SkinGeometry geometry; + private Entity leftParrot; private Entity rightParrot; diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index a5ed47660b6..ac6dad6e625 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -107,7 +107,7 @@ public class GeyserSession implements CommandSender { private InventoryCache inventoryCache; private ScoreboardCache scoreboardCache; private WindowCache windowCache; - private Object2LongMap skullCache = new Object2LongOpenHashMap() {}; + private Map skullCache = new HashMap<>(); @Setter private TeleportCache teleportCache; 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..90ef513bbc6 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 @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.bedrock; +import com.nukkitx.protocol.bedrock.data.EntityFlag; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -33,6 +34,8 @@ import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; +import java.util.concurrent.TimeUnit; + @Translator(packet = SetLocalPlayerAsInitializedPacket.class) public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslator { @Override @@ -48,6 +51,18 @@ public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession se SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session)); } } + + // Send Skulls + for (PlayerEntity entity : session.getSkullCache().values()) { + entity.spawnEntity(session); + SkinUtils.requestAndHandleSkinAndCape(entity, session, (skinAndCape) -> { + session.getConnector().getGeneralThreadPool().schedule(() -> { + entity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); + entity.updateBedrockMetadata(session); + }, 500, TimeUnit.MILLISECONDS); //Delay 500 milliseconds to give the model time to load in + }); + } + } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java index f387daecfdd..5429dfcecd5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java @@ -82,7 +82,7 @@ public void translate(ServerPlayerListEntryPacket packet, GeyserSession session) playerEntity.setPlayerList(true); playerEntity.setValid(true); - PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(entry.getProfile(), playerEntity.getGeyserId()); + PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(playerEntity); if (self) { // Copy the entry with our identity instead. PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java index 3719185b197..7d8c424b5a3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java @@ -47,7 +47,7 @@ public void translate(ServerUnloadChunkPacket packet, GeyserSession session) { while (iterator.hasNext()) { Position position = iterator.next(); if (Math.floor(position.getX() / 16) == packet.getX() && Math.floor(position.getZ() / 16) == packet.getZ()) { - session.getEntityCache().getEntityByGeyserId(session.getSkullCache().get(position)).despawnEntity(session); + session.getSkullCache().get(position).despawnEntity(session); iterator.remove(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 1f5ed2f3f5a..99d2287a47b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -1,197 +1,170 @@ -/* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.connector.network.translators.world.block.entity; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.steveice10.mc.auth.data.GameProfile; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.github.steveice10.opennbt.tag.builtin.StringTag; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.nbt.CompoundTagBuilder; -import com.nukkitx.nbt.tag.ByteTag; -import com.nukkitx.nbt.tag.CompoundTag; -import com.nukkitx.nbt.tag.FloatTag; -import com.nukkitx.nbt.tag.Tag; -import com.nukkitx.protocol.bedrock.data.*; -import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; -import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.entity.PlayerEntity; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.utils.SkinProvider; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -@BlockEntity(name = "Skull", regex = "skull") -public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - public static final boolean ALLOW_CUSTOM_SKULLS = GeyserConnector.getInstance().getConfig().isAllowCustomSkulls(); - - @Override - public boolean isBlock(BlockState blockState) { - return BlockStateValues.getSkullVariant(blockState) != -1; - } - - @Override - public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) { - List> tags = new ArrayList<>(); - byte skullVariant = BlockStateValues.getSkullVariant(blockState); - float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; - // Just in case... - if (skullVariant == -1) skullVariant = 0; - tags.add(new FloatTag("Rotation", rotation)); - tags.add(new ByteTag("SkullType", skullVariant)); - return tags; - } - - @Override - public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder(); - tagBuilder.floatTag("Rotation", 0); - tagBuilder.byteTag("SkullType", (byte) 0); - return tagBuilder.buildRootTag(); - } - - public static SerializedSkin getSkin(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, GeyserSession session) { - if (tag.contains("Owner") && !session.getSkullCache().containsKey(tag)) { - com.github.steveice10.opennbt.tag.builtin.CompoundTag owner = tag.get("Owner"); - com.github.steveice10.opennbt.tag.builtin.CompoundTag Properties = owner.get("Properties"); - ListTag textures = Properties.get("textures"); - LinkedHashMap tag1 = (LinkedHashMap) textures.get(0).getValue(); - StringTag texture = (StringTag) tag1.get("Value"); - byte[] decoded = Base64.getDecoder().decode(texture.getValue().getBytes()); - String url = new String(decoded); - - ObjectMapper objectMapper = new ObjectMapper(); - try { - JsonNode jsonNodeRoot = objectMapper.readTree(url); - JsonNode jsonNodeUrl = jsonNodeRoot.get("textures").get("SKIN").get("url"); - CompletableFuture skinCompletableFuture = SkinProvider.requestSkin(UUID.randomUUID(), jsonNodeUrl.asText(), false); - SkinProvider.Skin skin = skinCompletableFuture.get(); - SerializedSkin serializedSkin = SerializedSkin.of( - "Steve", SkinProvider.SkinGeometry.getSkull().getGeometryName(), ImageData.of(skin.getSkinData()), Collections.emptyList(), - ImageData.EMPTY, SkinProvider.SkinGeometry.getSkull().getGeometryData(), "", true, false, false, "", UUID.randomUUID().toString() - ); - return serializedSkin; - } catch (Exception e) { - e.printStackTrace(); - } - } else { - return null; - } - return null; - } - - public static void spawnPlayer(GeyserSession session, com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) { - SerializedSkin skin = getSkin(tag, session); - float x = (int) tag.get("x").getValue() + .5f; - float y = (int) tag.get("y").getValue() - .01f; - float z = (int) tag.get("z").getValue() + .5f; - float rotation = 0f; - - if (BlockStateValues.getSkullRotation(blockState) == -1) { - y += 0.25f; - switch (BlockStateValues.getWallSkullDirection().get(blockState)) { - case "north": - rotation = 180f; - z += 0.24; - break; - case "south": - rotation = 0; - z -= 0.24f; - break; - case "west": - rotation = 90; - x += 0.24f; - break; - case "east": - rotation = 270; - x -= 0.24f; - break; - } - } else { - rotation = (180f + (BlockStateValues.getSkullRotation(blockState) * 22.5f)) % 360; - } - - UUID uuid = UUID.randomUUID(); - long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); - - PlayerEntity player = new PlayerEntity(new GameProfile(uuid, ""), 1, geyserId, Vector3f.from(x), Vector3f.from(y), Vector3f.from(z)); - - //Creates a fake player to be the custom skull - PlayerSkinPacket playerSkinPacket = new PlayerSkinPacket(); - playerSkinPacket.setOldSkinName("OldName"); - playerSkinPacket.setNewSkinName("NewName"); - playerSkinPacket.setSkin(skin); - playerSkinPacket.setUuid(uuid); - - //Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow - EntityDataMap metadata = new EntityDataMap(); - metadata.put(EntityData.SCALE, 1.08f); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f); - metadata.getOrCreateFlags().setFlag(EntityFlag.INVISIBLE, true); //Set invisible so you don't see Steve while the model changes to the custom skull - - player.setMetadata(metadata); - player.spawnEntity(session); - - //Required for the fake player to show up at all - AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); - addPlayerPacket.setUuid(uuid); - addPlayerPacket.setUsername(""); - addPlayerPacket.setRuntimeEntityId(geyserId); - addPlayerPacket.setUniqueEntityId(geyserId); - addPlayerPacket.setPosition(Vector3f.from(x, y, z)); - addPlayerPacket.setRotation(Vector3f.from(0f, 0f, 0f)); - addPlayerPacket.getMetadata().putAll(metadata); - - //Send all the packets back to player - session.sendUpstreamPacket(playerSkinPacket); - session.sendUpstreamPacket(addPlayerPacket); - - player.updateBedrockAttributes(session); - player.moveAbsolute(session, Vector3f.from(x, y, z), rotation, 0, true, false); - session.getSkullCache().put((new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue())), player.getGeyserId()); - session.getConnector().getGeneralThreadPool().schedule(() -> { - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false); - player.updateBedrockMetadata(session); - }, 500, TimeUnit.MILLISECONDS); //Delay 500 milliseconds to give the model time to load in - } - - public static boolean containsCustomSkull(Position position, GeyserSession session) { - return session.getSkullCache().containsKey(position); - } -} +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.world.block.entity; + +import com.github.steveice10.mc.auth.data.GameProfile; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.ByteTag; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.nbt.tag.FloatTag; +import com.nukkitx.nbt.tag.Tag; +import com.nukkitx.protocol.bedrock.data.*; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.entity.PlayerEntity; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.utils.SkinProvider; +import org.geysermc.connector.utils.SkinUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@BlockEntity(name = "Skull", regex = "skull") +public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { + public static final boolean ALLOW_CUSTOM_SKULLS = GeyserConnector.getInstance().getConfig().isAllowCustomSkulls(); + + @Override + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getSkullVariant(blockState) != -1; + } + + @Override + public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) { + List> tags = new ArrayList<>(); + byte skullVariant = BlockStateValues.getSkullVariant(blockState); + float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; + // Just in case... + if (skullVariant == -1) skullVariant = 0; + tags.add(new FloatTag("Rotation", rotation)); + tags.add(new ByteTag("SkullType", skullVariant)); + return tags; + } + + @Override + public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { + return null; + } + + @Override + public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) { + CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder(); + tagBuilder.floatTag("Rotation", 0); + tagBuilder.byteTag("SkullType", (byte) 0); + return tagBuilder.buildRootTag(); + } + + public static GameProfile getProfile(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, GeyserSession session) { + if (tag.contains("Owner") && !session.getSkullCache().containsKey(tag)) { + com.github.steveice10.opennbt.tag.builtin.CompoundTag owner = tag.get("Owner"); + com.github.steveice10.opennbt.tag.builtin.CompoundTag Properties = owner.get("Properties"); + + ListTag textures = Properties.get("textures"); + LinkedHashMap tag1 = (LinkedHashMap) textures.get(0).getValue(); + StringTag texture = (StringTag) tag1.get("Value"); + + List properties = new ArrayList<>(); + + GameProfile gameProfile = new GameProfile(UUID.randomUUID(), ""); + properties.add(new GameProfile.Property("textures", texture.getValue())); + gameProfile.setProperties(properties); + return gameProfile; + } + return null; + } + + public static void spawnPlayer(GeyserSession session, com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) { + float x = (int) tag.get("x").getValue() + .5f; + float y = (int) tag.get("y").getValue() - .01f; + float z = (int) tag.get("z").getValue() + .5f; + float rotation = 0f; + + if (BlockStateValues.getSkullRotation(blockState) == -1) { + y += 0.25f; + switch (BlockStateValues.getWallSkullDirection().get(blockState)) { + case "north": + rotation = 180f; + z += 0.24; + break; + case "south": + rotation = 0; + z -= 0.24f; + break; + case "west": + rotation = 90; + x += 0.24f; + break; + case "east": + rotation = 270; + x -= 0.24f; + break; + } + } else { + rotation = (180f + ((BlockStateValues.getSkullRotation(blockState)) * 22.5f)) % 360; + } + + long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); + + GameProfile gameProfile = getProfile(tag, session); + Vector3f rotationVector = Vector3f.from(rotation, 0, rotation); + + PlayerEntity player = new PlayerEntity(gameProfile, 1, geyserId, Vector3f.from(x, y, z), Vector3f.ZERO, rotationVector ); + player.setPlayerList(false); + player.setGeometry(SkinProvider.SkinGeometry.getSkull()); + + //Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow + EntityDataMap metadata = new EntityDataMap(); + metadata.put(EntityData.SCALE, 1.08f); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f); + metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f); + metadata.getOrCreateFlags().setFlag(EntityFlag.CAN_SHOW_NAME, false); + metadata.getOrCreateFlags().setFlag(EntityFlag.INVISIBLE, true); + + player.setMetadata(metadata); + + // Cache entity + session.getSkullCache().put(new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue()), player); + + // Only send to session if we are initialized, otherwise it will happen then. + if (session.getUpstream().isInitialized()) { + player.spawnEntity(session); + SkinUtils.requestAndHandleSkinAndCape(player, session, (skinAndCape -> { + session.getConnector().getGeneralThreadPool().schedule(() -> { + player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); + player.updateBedrockMetadata(session); + }, 500, TimeUnit.MILLISECONDS); //Delay 500 milliseconds to give the model time to load in + })); + } + } + + public static boolean containsCustomSkull(Position position, GeyserSession session) { + return session.getSkullCache().containsKey(position); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 7ddf8313e37..27974a2e690 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -204,7 +204,7 @@ public static void updateBlock(GeyserSession session, BlockState blockState, Vec if (SkullBlockEntityTranslator.containsCustomSkull(new Position(position.getX(), position.getY(), position.getZ()), session) && blockState.equals(AIR)) { Position skullPosition = new Position(position.getX(), position.getY(), position.getZ()); RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); - removeEntityPacket.setUniqueEntityId(session.getSkullCache().get(skullPosition)); + removeEntityPacket.setUniqueEntityId(session.getSkullCache().get(skullPosition).getGeyserId()); session.sendUpstreamPacket(removeEntityPacket); session.getSkullCache().remove(skullPosition); } 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..f1a0e12d79c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -49,18 +49,21 @@ public class SkinUtils { - public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) { - GameProfileData data = GameProfileData.from(profile); + public static PlayerListPacket.Entry buildCachedEntry(PlayerEntity playerEntity) { + GameProfileData data = GameProfileData.from(playerEntity.getProfile()); SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl()); - SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); + SkinProvider.SkinGeometry geometry = playerEntity.getGeometry(); + if (geometry == null) { + geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); + } return buildEntryManually( - profile.getId(), - profile.getName(), - geyserId, - profile.getIdAsString(), - SkinProvider.getCachedSkin(profile.getId()).getSkinData(), + playerEntity.getProfile().getId(), + playerEntity.getProfile().getName(), + playerEntity.getGeyserId(), + playerEntity.getProfile().getIdAsString(), + SkinProvider.getCachedSkin(playerEntity.getProfile().getId()).getSkinData(), cape.getCapeId(), cape.getCapeData(), geometry.getGeometryName(), @@ -157,24 +160,31 @@ public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSessio SkinProvider.Skin skin = skinAndCape.getSkin(); SkinProvider.Cape cape = skinAndCape.getCape(); - if (cape.isFailed()) { - cape = SkinProvider.getOrDefault(SkinProvider.requestBedrockCape( - entity.getUuid(), false - ), SkinProvider.EMPTY_CAPE, 3); + if (!entity.getUsername().isEmpty()) { + if (cape.isFailed()) { + cape = SkinProvider.getOrDefault(SkinProvider.requestBedrockCape( + entity.getUuid(), false + ), SkinProvider.EMPTY_CAPE, 3); + } + + if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { + cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape( + cape, entity.getUuid(), + entity.getUsername(), false + ), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3); + } } - if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { - cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape( - cape, entity.getUuid(), - entity.getUsername(), false - ), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3); + SkinProvider.SkinGeometry geometry = entity.getGeometry(); + if (geometry == null) { + geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); } - SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); geometry = SkinProvider.getOrDefault(SkinProvider.requestBedrockGeometry( geometry, entity.getUuid(), false ), geometry, 3); + // Not a bedrock player check for ears if (geometry.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_EARS) { boolean isEars = false;