mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
[FEATURE][EXPERIMENTAL] Support slim skins
This commit is contained in:
parent
b0538abe63
commit
4dd30faf7e
8 changed files with 139 additions and 20 deletions
|
@ -0,0 +1,44 @@
|
|||
package pro.gravit.launchserver.auth.texture;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.HTTPRequest;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.profiles.Texture;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
public class JsonTextureProvider extends TextureProvider {
|
||||
public String url;
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
//None
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getCloakTexture(UUID uuid, String username, String client) throws IOException {
|
||||
logger.warn("Ineffective get cloak texture for {}", username);
|
||||
return getTextures(uuid, username, client).cloak;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getSkinTexture(UUID uuid, String username, String client) throws IOException {
|
||||
logger.warn("Ineffective get skin texture for {}", username);
|
||||
return getTextures(uuid, username, client).skin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
||||
try {
|
||||
var result = HTTPRequest.jsonRequest(null, "GET", new URL(RequestTextureProvider.getTextureURL(url, uuid, username, client)));
|
||||
return Launcher.gsonManager.gson.fromJson(result, SkinAndCloakTextures.class);
|
||||
} catch (IOException e) {
|
||||
logger.error("JsonTextureProvider", e);
|
||||
return new SkinAndCloakTextures(null, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ private static Texture getTexture(String url, Path local, boolean cloak) throws
|
|||
}
|
||||
}
|
||||
|
||||
private static String getTextureURL(String url, UUID uuid, String username, String client) {
|
||||
public static String getTextureURL(String url, UUID uuid, String username, String client) {
|
||||
return CommonHelper.replace(url, "username", IOHelper.urlEncode(username),
|
||||
"uuid", IOHelper.urlEncode(uuid.toString()), "hash", IOHelper.urlEncode(Launcher.toHash(uuid)),
|
||||
"client", IOHelper.urlEncode(client == null ? "unknown" : client));
|
||||
|
|
|
@ -17,6 +17,7 @@ public static void registerProviders() {
|
|||
|
||||
// Auth providers that doesn't do nothing :D
|
||||
providers.register("request", RequestTextureProvider.class);
|
||||
providers.register("json", JsonTextureProvider.class);
|
||||
registredProv = true;
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +30,34 @@ public static void registerProviders() {
|
|||
|
||||
|
||||
public abstract Texture getSkinTexture(UUID uuid, String username, String client) throws IOException;
|
||||
|
||||
public static class SkinAndCloakTextures {
|
||||
public final Texture skin;
|
||||
public final Texture cloak;
|
||||
|
||||
public SkinAndCloakTextures(Texture skin, Texture cloak) {
|
||||
this.skin = skin;
|
||||
this.cloak = cloak;
|
||||
}
|
||||
}
|
||||
|
||||
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
||||
|
||||
Texture skin;
|
||||
try {
|
||||
skin = getSkinTexture(uuid, username, client);
|
||||
} catch (IOException e) {
|
||||
skin = null;
|
||||
}
|
||||
|
||||
// Get cloak texture
|
||||
Texture cloak;
|
||||
try {
|
||||
cloak = getCloakTexture(uuid, username, client);
|
||||
} catch (IOException e) {
|
||||
cloak = null;
|
||||
}
|
||||
|
||||
return new SkinAndCloakTextures(skin, cloak);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
||||
import pro.gravit.launcher.profiles.Texture;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.password.*;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
|
@ -309,23 +308,10 @@ public PlayerProfile getPlayerProfile(AuthProviderPair pair, User user) {
|
|||
|
||||
private PlayerProfile getPlayerProfile(UUID uuid, String username, String client, TextureProvider textureProvider) {
|
||||
// Get skin texture
|
||||
Texture skin;
|
||||
try {
|
||||
skin = textureProvider.getSkinTexture(uuid, username, client);
|
||||
} catch (IOException e) {
|
||||
skin = null;
|
||||
}
|
||||
|
||||
// Get cloak texture
|
||||
Texture cloak;
|
||||
try {
|
||||
cloak = textureProvider.getCloakTexture(uuid, username, client);
|
||||
} catch (IOException e) {
|
||||
cloak = null;
|
||||
}
|
||||
TextureProvider.SkinAndCloakTextures textures = textureProvider.getTextures(uuid, username, client);
|
||||
|
||||
// Return combined profile
|
||||
return new PlayerProfile(uuid, username, skin, cloak);
|
||||
return new PlayerProfile(uuid, username, textures.skin, textures.cloak);
|
||||
}
|
||||
|
||||
public AuthRequest.AuthPasswordInterface decryptPassword(AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||
|
|
|
@ -25,9 +25,15 @@ public final class Launcher {
|
|||
|
||||
public static final String SKIN_DIGEST_PROPERTY = "skinDigest";
|
||||
|
||||
public static final String SKIN_METADATA_PROPERTY = "skinMetadata";
|
||||
|
||||
public static final String CLOAK_URL_PROPERTY = "cloakURL";
|
||||
|
||||
public static final String CLOAK_DIGEST_PROPERTY = "cloakDigest";
|
||||
|
||||
public static final String CLOAK_METADATA_PROPERTY = "cloakMetadata";
|
||||
|
||||
|
||||
// Used to determine from clientside is launched from launcher
|
||||
public static final AtomicBoolean LAUNCHED = new AtomicBoolean(false);
|
||||
public static final int PROTOCOL_MAGIC_LEGACY = 0x724724_00 + 24;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Texture extends StreamObject {
|
||||
|
@ -22,11 +23,14 @@ public final class Texture extends StreamObject {
|
|||
|
||||
public final byte[] digest;
|
||||
|
||||
public final Map<String, String> metadata;
|
||||
|
||||
|
||||
@Deprecated
|
||||
public Texture(HInput input) throws IOException {
|
||||
url = IOHelper.verifyURL(input.readASCII(2048));
|
||||
digest = input.readByteArray(-DIGEST_ALGO.bytes);
|
||||
metadata = null;
|
||||
}
|
||||
|
||||
public Texture(String url, boolean cloak) throws IOException {
|
||||
|
@ -43,6 +47,7 @@ public Texture(String url, boolean cloak) throws IOException {
|
|||
|
||||
// Get digest of texture
|
||||
digest = SecurityHelper.digest(DIGEST_ALGO, new URL(url));
|
||||
metadata = null; // May be auto-detect?
|
||||
}
|
||||
|
||||
public Texture(String url, Path local, boolean cloak) throws IOException {
|
||||
|
@ -51,12 +56,20 @@ public Texture(String url, Path local, boolean cloak) throws IOException {
|
|||
IOHelper.readTexture(input, cloak); // Verify texture
|
||||
}
|
||||
this.digest = Objects.requireNonNull(SecurityHelper.digest(DIGEST_ALGO, local), "digest");
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
|
||||
public Texture(String url, byte[] digest) {
|
||||
this.url = IOHelper.verifyURL(url);
|
||||
this.digest = Objects.requireNonNull(digest, "digest");
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
public Texture(String url, byte[] digest, Map<String, String> metadata) {
|
||||
this.url = url;
|
||||
this.digest = digest;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class MinecraftProfileTexture {
|
||||
|
@ -10,6 +11,7 @@ public class MinecraftProfileTexture {
|
|||
// Instance
|
||||
private final String url;
|
||||
private final String hash;
|
||||
private final Map<String, String> metadata;
|
||||
|
||||
public MinecraftProfileTexture(String url) {
|
||||
this(url, baseName(url));
|
||||
|
@ -18,6 +20,13 @@ public MinecraftProfileTexture(String url) {
|
|||
public MinecraftProfileTexture(String url, String hash) {
|
||||
this.url = url;
|
||||
this.hash = hash;
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
public MinecraftProfileTexture(String url, String hash, Map<String, String> metadata) {
|
||||
this.url = url;
|
||||
this.hash = hash;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
private static String baseName(String url) {
|
||||
|
@ -37,7 +46,10 @@ public String getHash() {
|
|||
}
|
||||
|
||||
public String getMetadata(String key) {
|
||||
return null;
|
||||
if (metadata == null) {
|
||||
return null;
|
||||
}
|
||||
return metadata.get(key);
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package com.mojang.authlib.yggdrasil;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mojang.authlib.AuthenticationService;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
|
@ -21,6 +23,7 @@
|
|||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Base64;
|
||||
import java.util.EnumMap;
|
||||
|
@ -29,6 +32,7 @@
|
|||
|
||||
public class YggdrasilMinecraftSessionService extends BaseMinecraftSessionService {
|
||||
public static final boolean NO_TEXTURES = Boolean.getBoolean("launcher.com.mojang.authlib.noTextures");
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public YggdrasilMinecraftSessionService(AuthenticationService service) {
|
||||
super(service);
|
||||
|
@ -53,6 +57,10 @@ public static void fillTextureProperties(GameProfile profile, PlayerProfile pp)
|
|||
if (pp.skin != null) {
|
||||
properties.put(Launcher.SKIN_URL_PROPERTY, new Property(Launcher.SKIN_URL_PROPERTY, pp.skin.url, ""));
|
||||
properties.put(Launcher.SKIN_DIGEST_PROPERTY, new Property(Launcher.SKIN_DIGEST_PROPERTY, SecurityHelper.toHex(pp.skin.digest), ""));
|
||||
if (pp.skin.metadata != null) {
|
||||
String metadata = serializeMetadataMap(pp.skin.metadata);
|
||||
properties.put(Launcher.SKIN_METADATA_PROPERTY, new Property(Launcher.SKIN_METADATA_PROPERTY, metadata, ""));
|
||||
}
|
||||
if (debug) {
|
||||
LogHelper.debug("fillTextureProperties, Has skin texture for username '%s'", profile.getName());
|
||||
}
|
||||
|
@ -60,12 +68,29 @@ public static void fillTextureProperties(GameProfile profile, PlayerProfile pp)
|
|||
if (pp.cloak != null) {
|
||||
properties.put(Launcher.CLOAK_URL_PROPERTY, new Property(Launcher.CLOAK_URL_PROPERTY, pp.cloak.url, ""));
|
||||
properties.put(Launcher.CLOAK_DIGEST_PROPERTY, new Property(Launcher.CLOAK_DIGEST_PROPERTY, SecurityHelper.toHex(pp.cloak.digest), ""));
|
||||
if (pp.cloak.metadata != null) {
|
||||
String metadata = serializeMetadataMap(pp.cloak.metadata);
|
||||
properties.put(Launcher.CLOAK_METADATA_PROPERTY, new Property(Launcher.SKIN_METADATA_PROPERTY, metadata, ""));
|
||||
}
|
||||
if (debug) {
|
||||
LogHelper.debug("fillTextureProperties, Has cloak texture for username '%s'", profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String serializeMetadataMap(Map<String, String> map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return gson.toJson(map);
|
||||
}
|
||||
|
||||
private static Map<String, String> deserializeMetadataMap(String value) {
|
||||
Type typeOfMap = new TypeToken<Map<String, String>>() {
|
||||
}.getType();
|
||||
return gson.fromJson(value, typeOfMap);
|
||||
}
|
||||
|
||||
private static void getTexturesMojang(Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures, String texturesBase64, GameProfile profile) {
|
||||
// Decode textures payload
|
||||
JsonObject texturesJSON;
|
||||
|
@ -148,14 +173,16 @@ public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(Ga
|
|||
// Add skin URL to textures map
|
||||
Property skinURL = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_URL_PROPERTY), null);
|
||||
Property skinDigest = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_DIGEST_PROPERTY), null);
|
||||
Property skinMetadata = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_METADATA_PROPERTY), null);
|
||||
if (skinURL != null && skinDigest != null)
|
||||
textures.put(MinecraftProfileTexture.Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue()));
|
||||
textures.put(MinecraftProfileTexture.Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue(), skinMetadata == null ? null : deserializeMetadataMap(skinMetadata.getValue())));
|
||||
|
||||
// Add cloak URL to textures map
|
||||
Property cloakURL = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_URL_PROPERTY), null);
|
||||
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_DIGEST_PROPERTY), null);
|
||||
Property cloakMetadata = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_METADATA_PROPERTY), null);
|
||||
if (cloakURL != null && cloakDigest != null)
|
||||
textures.put(MinecraftProfileTexture.Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue()));
|
||||
textures.put(MinecraftProfileTexture.Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue(), cloakMetadata == null ? null : deserializeMetadataMap(cloakMetadata.getValue())));
|
||||
|
||||
// Try to find missing textures in textures payload (now always true because launcher is not passing elytra skins)
|
||||
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
|
||||
|
|
Loading…
Reference in a new issue