Fixes errors related to skins

This commit is contained in:
Tim203 2019-11-19 21:31:24 +01:00
parent 51c1792d67
commit 068033aeaa
5 changed files with 30 additions and 27 deletions

View file

@ -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));
}

View file

@ -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));
}
}
}

View file

@ -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();

View file

@ -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;

View file

@ -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" : "")),
""