2016-02-27 15:22:35 +08:00
|
|
|
package customskinloader.loader;
|
|
|
|
|
|
2022-07-28 10:59:12 +08:00
|
|
|
import java.nio.charset.StandardCharsets;
|
2019-09-18 22:15:58 +08:00
|
|
|
import java.util.Collections;
|
2021-05-25 12:55:42 +08:00
|
|
|
import java.util.List;
|
2016-04-30 22:12:26 +08:00
|
|
|
import java.util.Map;
|
2016-07-31 22:00:46 +08:00
|
|
|
import java.util.UUID;
|
2023-01-26 19:34:58 +08:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2016-02-27 15:22:35 +08:00
|
|
|
|
2016-08-28 20:55:54 +08:00
|
|
|
import com.google.common.collect.Iterables;
|
2021-05-25 12:55:42 +08:00
|
|
|
import com.google.common.collect.Lists;
|
2021-04-24 10:33:18 +08:00
|
|
|
import com.google.common.collect.Maps;
|
2016-02-27 15:22:35 +08:00
|
|
|
import com.google.gson.Gson;
|
2016-07-31 22:00:46 +08:00
|
|
|
import com.google.gson.GsonBuilder;
|
|
|
|
|
import com.mojang.authlib.GameProfile;
|
2016-02-27 15:22:35 +08:00
|
|
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
2016-08-28 20:55:54 +08:00
|
|
|
import com.mojang.authlib.properties.Property;
|
2018-02-12 15:02:12 +08:00
|
|
|
import com.mojang.authlib.properties.PropertyMap;
|
|
|
|
|
import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse;
|
2016-08-28 20:55:54 +08:00
|
|
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
2016-07-31 22:00:46 +08:00
|
|
|
import com.mojang.util.UUIDTypeAdapter;
|
2016-02-27 15:22:35 +08:00
|
|
|
import customskinloader.CustomSkinLoader;
|
|
|
|
|
import customskinloader.config.SkinSiteProfile;
|
2021-04-24 10:33:18 +08:00
|
|
|
import customskinloader.plugin.ICustomSkinLoaderPlugin;
|
2016-07-31 22:00:46 +08:00
|
|
|
import customskinloader.profile.ModelManager0;
|
|
|
|
|
import customskinloader.profile.UserProfile;
|
2016-11-06 12:00:36 +08:00
|
|
|
import customskinloader.utils.HttpRequestUtil;
|
2021-04-24 10:33:18 +08:00
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
|
2021-05-25 12:55:42 +08:00
|
|
|
public class MojangAPILoader implements ICustomSkinLoaderPlugin, ProfileLoader.IProfileLoader {
|
2021-04-24 10:33:18 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ProfileLoader.IProfileLoader getProfileLoader() {
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-05-25 12:55:42 +08:00
|
|
|
public List<IDefaultProfile> getDefaultProfiles() {
|
|
|
|
|
return Lists.newArrayList(new Mojang(this));
|
2021-04-24 10:33:18 +08:00
|
|
|
}
|
|
|
|
|
|
2021-05-25 12:55:42 +08:00
|
|
|
public abstract static class DefaultProfile implements ICustomSkinLoaderPlugin.IDefaultProfile {
|
|
|
|
|
protected final MojangAPILoader loader;
|
|
|
|
|
|
|
|
|
|
public DefaultProfile(MojangAPILoader loader) {
|
|
|
|
|
this.loader = loader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void updateSkinSiteProfile(SkinSiteProfile ssp) {
|
2022-07-28 10:59:12 +08:00
|
|
|
ssp.type = this.loader.getName();
|
|
|
|
|
ssp.apiRoot = this.getAPIRoot();
|
2021-05-25 12:55:42 +08:00
|
|
|
ssp.sessionRoot = this.getSessionRoot();
|
|
|
|
|
}
|
2021-04-24 10:33:18 +08:00
|
|
|
|
2021-05-25 12:55:42 +08:00
|
|
|
public abstract String getAPIRoot();
|
2022-07-28 10:59:12 +08:00
|
|
|
|
2021-05-25 12:55:42 +08:00
|
|
|
public abstract String getSessionRoot();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Mojang extends MojangAPILoader.DefaultProfile {
|
2022-07-28 10:59:12 +08:00
|
|
|
public Mojang(MojangAPILoader loader) {
|
|
|
|
|
super(loader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String getName() {
|
|
|
|
|
return "Mojang";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getPriority() {
|
|
|
|
|
return 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String getAPIRoot() {
|
|
|
|
|
return getMojangApiRoot();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String getSessionRoot() {
|
|
|
|
|
return getMojangSessionRoot();
|
|
|
|
|
}
|
2021-04-24 10:33:18 +08:00
|
|
|
}
|
2016-02-27 15:22:35 +08:00
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
@Override
|
2022-07-28 10:59:12 +08:00
|
|
|
public UserProfile loadProfile(SkinSiteProfile ssp, GameProfile gameProfile) {
|
2019-09-18 22:15:58 +08:00
|
|
|
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> map = getTextures(gameProfile);
|
|
|
|
|
if (!map.isEmpty()) {
|
2018-02-12 12:20:28 +08:00
|
|
|
CustomSkinLoader.logger.info("Default profile will be used.");
|
|
|
|
|
return ModelManager0.toUserProfile(map);
|
|
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
String username = gameProfile.getName();
|
2023-01-26 19:34:58 +08:00
|
|
|
GameProfile newGameProfile = loadGameProfileCached(ssp.apiRoot, username);
|
2019-09-18 22:15:58 +08:00
|
|
|
if (newGameProfile == null) {
|
|
|
|
|
CustomSkinLoader.logger.info("Profile not found.(" + username + "'s profile not found.)");
|
2018-02-12 12:20:28 +08:00
|
|
|
return null;
|
|
|
|
|
}
|
2019-09-20 16:33:04 +08:00
|
|
|
newGameProfile = fillGameProfile(ssp.sessionRoot, newGameProfile);
|
2019-09-18 22:15:58 +08:00
|
|
|
map = getTextures(newGameProfile);
|
|
|
|
|
if (!map.isEmpty()) {
|
2018-02-12 12:20:28 +08:00
|
|
|
gameProfile.getProperties().putAll(newGameProfile.getProperties());
|
|
|
|
|
return ModelManager0.toUserProfile(map);
|
|
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
CustomSkinLoader.logger.info("Profile not found.(" + username + " doesn't have skin/cape.)");
|
2018-02-12 12:20:28 +08:00
|
|
|
return null;
|
|
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
2023-01-26 19:34:58 +08:00
|
|
|
private static final Map<String, GameProfile> gameProfileCache = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
public static GameProfile loadGameProfileCached(String apiRoot, String username) {
|
|
|
|
|
return gameProfileCache.computeIfAbsent(apiRoot + " " + username,
|
|
|
|
|
ignored -> loadGameProfile(apiRoot, username));
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
//Username -> UUID
|
2019-09-18 22:15:58 +08:00
|
|
|
public static GameProfile loadGameProfile(String apiRoot, String username) {
|
|
|
|
|
//Doc (https://wiki.vg/Mojang_API#Playernames_-.3E_UUIDs)
|
|
|
|
|
Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
|
|
|
|
|
2021-04-24 10:33:18 +08:00
|
|
|
HttpRequestUtil.HttpResponce responce = HttpRequestUtil.makeHttpRequest(
|
2022-07-28 10:59:12 +08:00
|
|
|
new HttpRequestUtil.HttpRequest(apiRoot + "profiles/minecraft")
|
|
|
|
|
.setCacheTime(600).setPayload(gson.toJson(Collections.singletonList(username)))
|
2019-09-18 22:15:58 +08:00
|
|
|
);
|
2022-07-28 10:59:12 +08:00
|
|
|
if (StringUtils.isEmpty(responce.content)) {
|
2018-02-12 12:20:28 +08:00
|
|
|
return null;
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
|
|
|
|
GameProfile[] profiles = gson.fromJson(responce.content, GameProfile[].class);
|
2022-07-28 10:59:12 +08:00
|
|
|
if (profiles.length == 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
GameProfile gameProfile = profiles[0];
|
|
|
|
|
|
2022-07-28 10:59:12 +08:00
|
|
|
if (gameProfile.getId() == null) {
|
2018-02-12 12:20:28 +08:00
|
|
|
return null;
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
return new GameProfile(gameProfile.getId(), gameProfile.getName());
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
2022-07-28 10:59:12 +08:00
|
|
|
/**
|
|
|
|
|
* Get Mojang UUID by username.
|
|
|
|
|
*
|
|
|
|
|
* @param username username to query
|
2023-01-11 22:06:58 +08:00
|
|
|
* @param standard if - in UUID
|
2022-07-28 10:59:12 +08:00
|
|
|
* @return UUID in Mojang API style string. Returns {@code null} if username not found in Mojang API.
|
|
|
|
|
*/
|
2023-01-11 22:06:58 +08:00
|
|
|
public static String getMojangUuidByUsername(String username, boolean standard) {
|
2023-01-26 19:34:58 +08:00
|
|
|
GameProfile profile = loadGameProfileCached(getMojangApiRoot(), username);
|
2022-07-28 10:59:12 +08:00
|
|
|
if (profile == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2023-01-11 22:06:58 +08:00
|
|
|
UUID id = profile.getId();
|
|
|
|
|
return standard ? id.toString() : UUIDTypeAdapter.fromUUID(id);
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
|
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
//UUID -> Profile
|
2019-09-18 22:15:58 +08:00
|
|
|
public static GameProfile fillGameProfile(String sessionRoot, GameProfile profile) {
|
2018-02-12 12:20:28 +08:00
|
|
|
//Doc (http://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape)
|
2021-04-24 10:33:18 +08:00
|
|
|
HttpRequestUtil.HttpResponce responce = HttpRequestUtil.makeHttpRequest(
|
2022-07-28 10:59:12 +08:00
|
|
|
new HttpRequestUtil.HttpRequest(sessionRoot + "session/minecraft/profile/"
|
|
|
|
|
+ UUIDTypeAdapter.fromUUID(profile.getId())).setCacheTime(90));
|
|
|
|
|
if (StringUtils.isEmpty(responce.content)) {
|
2018-02-12 12:20:28 +08:00
|
|
|
return profile;
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
|
|
|
|
Gson gson = new GsonBuilder()
|
2022-07-28 10:59:12 +08:00
|
|
|
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
|
|
|
|
.registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer())
|
|
|
|
|
.create();
|
2019-09-18 22:15:58 +08:00
|
|
|
MinecraftProfilePropertiesResponse propertiesResponce = gson.fromJson(responce.content, MinecraftProfilePropertiesResponse.class);
|
|
|
|
|
GameProfile newGameProfile = new GameProfile(propertiesResponce.getId(), propertiesResponce.getName());
|
2018-02-12 15:02:12 +08:00
|
|
|
newGameProfile.getProperties().putAll(propertiesResponce.getProperties());
|
2019-09-18 22:15:58 +08:00
|
|
|
|
2018-02-12 15:02:12 +08:00
|
|
|
return newGameProfile;
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
|
|
|
|
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile gameProfile) {
|
2022-07-28 10:59:12 +08:00
|
|
|
if (gameProfile == null) {
|
2018-06-25 00:34:32 +08:00
|
|
|
return Maps.newHashMap();
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
Property textureProperty = Iterables.getFirst(gameProfile.getProperties().get("textures"), null);
|
2022-07-28 10:59:12 +08:00
|
|
|
if (textureProperty == null) {
|
2018-06-25 00:34:32 +08:00
|
|
|
return Maps.newHashMap();
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
String value = textureProperty.getValue();
|
2022-07-28 10:59:12 +08:00
|
|
|
if (StringUtils.isBlank(value)) {
|
2018-06-25 00:34:32 +08:00
|
|
|
return Maps.newHashMap();
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
|
|
|
|
String json = new String(Base64.decodeBase64(value), StandardCharsets.UTF_8);
|
2019-09-18 22:15:58 +08:00
|
|
|
Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
|
|
|
|
MinecraftTexturesPayload result = gson.fromJson(json, MinecraftTexturesPayload.class);
|
2022-07-28 10:59:12 +08:00
|
|
|
if (result == null || result.getTextures() == null) {
|
2018-06-25 00:34:32 +08:00
|
|
|
return Maps.newHashMap();
|
2022-07-28 10:59:12 +08:00
|
|
|
}
|
2018-02-12 12:20:28 +08:00
|
|
|
return result.getTextures();
|
|
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean compare(SkinSiteProfile ssp0, SkinSiteProfile ssp1) {
|
2022-07-28 10:59:12 +08:00
|
|
|
return (!StringUtils.isNoneEmpty(ssp0.apiRoot) || ssp0.apiRoot.equalsIgnoreCase(ssp1.apiRoot)) ||
|
|
|
|
|
(!StringUtils.isNoneEmpty(ssp0.sessionRoot) || ssp0.sessionRoot.equalsIgnoreCase(ssp1.sessionRoot));
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2019-09-18 22:15:58 +08:00
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
@Override
|
|
|
|
|
public String getName() {
|
|
|
|
|
return "MojangAPI";
|
|
|
|
|
}
|
2016-09-24 20:47:08 +08:00
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
@Override
|
2019-09-18 22:15:58 +08:00
|
|
|
public void init(SkinSiteProfile ssp) {
|
2019-09-20 11:44:07 +08:00
|
|
|
//Init default api & session root for Mojang API
|
2019-09-18 22:50:04 +08:00
|
|
|
if (ssp.apiRoot == null)
|
2021-05-01 22:46:02 +08:00
|
|
|
ssp.apiRoot = getMojangApiRoot();
|
2019-09-20 16:33:04 +08:00
|
|
|
if (ssp.sessionRoot == null)
|
2021-05-01 22:46:02 +08:00
|
|
|
ssp.sessionRoot = getMojangSessionRoot();
|
2019-10-03 15:30:46 +08:00
|
|
|
}
|
|
|
|
|
|
2021-05-01 22:46:02 +08:00
|
|
|
// Prevent authlib-injector (https://github.com/yushijinhun/authlib-injector) from modifying these strings
|
2019-10-03 15:30:46 +08:00
|
|
|
private static final String MOJANG_API_ROOT = "https://api{DO_NOT_MODIFY}.mojang.com/";
|
2021-05-01 22:46:02 +08:00
|
|
|
private static final String MOJANG_SESSION_ROOT = "https://sessionserver{DO_NOT_MODIFY}.mojang.com/";
|
2019-09-18 22:50:04 +08:00
|
|
|
|
2019-10-03 15:30:46 +08:00
|
|
|
public static String getMojangApiRoot() {
|
|
|
|
|
return MOJANG_API_ROOT.replace("{DO_NOT_MODIFY}", "");
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2021-05-01 22:46:02 +08:00
|
|
|
|
|
|
|
|
public static String getMojangSessionRoot() {
|
|
|
|
|
return MOJANG_SESSION_ROOT.replace("{DO_NOT_MODIFY}", "");
|
|
|
|
|
}
|
2016-02-27 15:22:35 +08:00
|
|
|
}
|