mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge pull request #84 from Tim203/feature/1.13-bedrock
Fixes errors related to skins
This commit is contained in:
commit
beda8f24b4
5 changed files with 30 additions and 27 deletions
|
@ -15,7 +15,6 @@ public class BedrockPlayerInitializedTranslator extends PacketTranslator<SetLoca
|
||||||
|
|
||||||
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
||||||
if (!entity.isValid()) {
|
if (!entity.isValid()) {
|
||||||
entity.sendPlayer(session);
|
|
||||||
// async skin loading
|
// async skin loading
|
||||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,9 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
|
||||||
entity.setPosition(position);
|
entity.setPosition(position);
|
||||||
entity.setRotation(rotation);
|
entity.setRotation(rotation);
|
||||||
|
|
||||||
entity.sendPlayer(session);
|
|
||||||
// async skin loading
|
// async skin loading
|
||||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
if (session.getUpstream().isInitialized()) {
|
||||||
|
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.geysermc.connector.utils;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -19,11 +18,11 @@ public class ProvidedSkin {
|
||||||
try {
|
try {
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
Color color = new Color(image.getRGB(x, y), true);
|
int rgba = image.getRGB(x, y);
|
||||||
outputStream.write(color.getRed());
|
outputStream.write((rgba >> 16) & 0xFF); // Red
|
||||||
outputStream.write(color.getGreen());
|
outputStream.write((rgba >> 8) & 0xFF); // Green
|
||||||
outputStream.write(color.getBlue());
|
outputStream.write(rgba & 0xFF); // Blue
|
||||||
outputStream.write(color.getAlpha());
|
outputStream.write((rgba >> 24) & 0xFF); // Alpha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image.flush();
|
image.flush();
|
||||||
|
|
|
@ -22,12 +22,12 @@ public class SkinProvider {
|
||||||
public static final boolean ALLOW_THIRD_PARTY_CAPES = ((GeyserConnector)Geyser.getConnector()).getConfig().isAllowThirdPartyCapes();
|
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);
|
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 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, Skin> cachedSkins = new ConcurrentHashMap<>();
|
||||||
private static Map<UUID, CompletableFuture<Skin>> requestedSkins = 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, Cape> cachedCapes = new ConcurrentHashMap<>();
|
||||||
private static Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
|
private static Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -149,8 +149,11 @@ public class SkinProvider {
|
||||||
cape = requestImage(capeUrl, true);
|
cape = requestImage(capeUrl, true);
|
||||||
} catch (Exception ignored) {} // just ignore I guess
|
} catch (Exception ignored) {} // just ignore I guess
|
||||||
|
|
||||||
|
String[] urlSection = capeUrl.split("/"); // A real url is expected at this stage
|
||||||
|
|
||||||
return new Cape(
|
return new Cape(
|
||||||
capeUrl,
|
capeUrl,
|
||||||
|
urlSection[urlSection.length - 1], // get the texture id and use it as cape id
|
||||||
cape.length > 0 ? cape : EMPTY_CAPE.getCapeData(),
|
cape.length > 0 ? cape : EMPTY_CAPE.getCapeData(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
cape.length == 0
|
cape.length == 0
|
||||||
|
@ -209,13 +212,14 @@ public class SkinProvider {
|
||||||
public static class Skin {
|
public static class Skin {
|
||||||
private UUID skinOwner;
|
private UUID skinOwner;
|
||||||
private String textureUrl;
|
private String textureUrl;
|
||||||
private byte[] skinData = STEVE_SKIN;
|
private byte[] skinData;
|
||||||
private long requestedOn;
|
private long requestedOn;
|
||||||
private boolean updated;
|
private boolean updated;
|
||||||
|
|
||||||
private Skin(long requestedOn, String textureUrl) {
|
private Skin(long requestedOn, String textureUrl, byte[] skinData) {
|
||||||
this.requestedOn = requestedOn;
|
this.requestedOn = requestedOn;
|
||||||
this.textureUrl = textureUrl;
|
this.textureUrl = textureUrl;
|
||||||
|
this.skinData = skinData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +227,7 @@ public class SkinProvider {
|
||||||
@Getter
|
@Getter
|
||||||
public static class Cape {
|
public static class Cape {
|
||||||
private String textureUrl;
|
private String textureUrl;
|
||||||
|
private String capeId;
|
||||||
private byte[] capeData;
|
private byte[] capeData;
|
||||||
private long requestedOn;
|
private long requestedOn;
|
||||||
private boolean failed;
|
private boolean failed;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.function.Consumer;
|
||||||
public class SkinUtils {
|
public class SkinUtils {
|
||||||
public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) {
|
public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) {
|
||||||
GameProfileData data = GameProfileData.from(profile);
|
GameProfileData data = GameProfileData.from(profile);
|
||||||
|
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
|
||||||
|
|
||||||
return buildEntryManually(
|
return buildEntryManually(
|
||||||
profile.getId(),
|
profile.getId(),
|
||||||
|
@ -27,7 +28,8 @@ public class SkinUtils {
|
||||||
geyserId,
|
geyserId,
|
||||||
profile.getIdAsString(),
|
profile.getIdAsString(),
|
||||||
SkinProvider.getCachedSkin(profile.getId()).getSkinData(),
|
SkinProvider.getCachedSkin(profile.getId()).getSkinData(),
|
||||||
SkinProvider.getCachedCape(data.getCapeUrl()).getCapeData(),
|
cape.getCapeId(),
|
||||||
|
cape.getCapeData(),
|
||||||
getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
|
getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
@ -40,6 +42,7 @@ public class SkinUtils {
|
||||||
geyserId,
|
geyserId,
|
||||||
profile.getIdAsString(),
|
profile.getIdAsString(),
|
||||||
SkinProvider.STEVE_SKIN,
|
SkinProvider.STEVE_SKIN,
|
||||||
|
SkinProvider.EMPTY_CAPE.getCapeId(),
|
||||||
SkinProvider.EMPTY_CAPE.getCapeData(),
|
SkinProvider.EMPTY_CAPE.getCapeData(),
|
||||||
getLegacySkinGeometry("geometry.humanoid"),
|
getLegacySkinGeometry("geometry.humanoid"),
|
||||||
""
|
""
|
||||||
|
@ -47,18 +50,13 @@ public class SkinUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerListPacket.Entry buildEntryManually(UUID uuid, String username, long geyserId,
|
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) {
|
String geometryName, String geometryData) {
|
||||||
if (skinData == null || skinData.length == 0) {
|
SerializedSkin serializedSkin = SerializedSkin.of(
|
||||||
skinData = SkinProvider.EMPTY_SKIN.getSkinData();
|
skinId, geometryName, ImageData.of(skinData), Collections.emptyList(),
|
||||||
}
|
ImageData.of(capeData), geometryData, "", true, false, false, capeId, uuid.toString()
|
||||||
|
);
|
||||||
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, "", "");
|
|
||||||
|
|
||||||
PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
|
PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
|
||||||
entry.setName(username);
|
entry.setName(username);
|
||||||
|
@ -107,7 +105,7 @@ public class SkinUtils {
|
||||||
public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session,
|
public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session,
|
||||||
Consumer<SkinProvider.SkinAndCape> skinAndCapeConsumer) {
|
Consumer<SkinProvider.SkinAndCape> skinAndCapeConsumer) {
|
||||||
Geyser.getGeneralThreadPool().execute(() -> {
|
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())
|
SkinProvider.requestSkinAndCape(entity.getUuid(), data.getSkinUrl(), data.getCapeUrl())
|
||||||
.whenCompleteAsync((skinAndCape, throwable) -> {
|
.whenCompleteAsync((skinAndCape, throwable) -> {
|
||||||
|
@ -126,12 +124,13 @@ public class SkinUtils {
|
||||||
entity.setLastSkinUpdate(skin.getRequestedOn());
|
entity.setLastSkinUpdate(skin.getRequestedOn());
|
||||||
|
|
||||||
if (session.getUpstream().isInitialized()) {
|
if (session.getUpstream().isInitialized()) {
|
||||||
PlayerListPacket.Entry updatedEntry = SkinUtils.buildEntryManually(
|
PlayerListPacket.Entry updatedEntry = buildEntryManually(
|
||||||
entity.getUuid(),
|
entity.getUuid(),
|
||||||
entity.getUsername(),
|
entity.getUsername(),
|
||||||
entity.getGeyserId(),
|
entity.getGeyserId(),
|
||||||
entity.getUuid().toString(),
|
entity.getUuid().toString(),
|
||||||
skin.getSkinData(),
|
skin.getSkinData(),
|
||||||
|
cape.getCapeId(),
|
||||||
cape.getCapeData(),
|
cape.getCapeData(),
|
||||||
getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
|
getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
|
||||||
""
|
""
|
||||||
|
|
Loading…
Reference in a new issue