package customskinloader; import java.io.File; import java.util.Map; import java.util.concurrent.*; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type; import customskinloader.config.Config; import customskinloader.config.SkinSiteProfile; import customskinloader.loader.*; import customskinloader.profile.DynamicSkullManager; import customskinloader.profile.ModelManager0; import customskinloader.profile.ProfileCache; import customskinloader.profile.UserProfile; import customskinloader.utils.MinecraftUtil; /** * Custom skin loader mod for Minecraft. * @author (C) Jeremy Lam [JLChnToZ] 2013-2014 & Alexander Xia [xfl03] 2014-2020 * @version @MOD_FULL_VERSION@ */ public class CustomSkinLoader { public static final String CustomSkinLoader_VERSION="@MOD_VERSION@"; public static final String CustomSkinLoader_FULL_VERSION="@MOD_FULL_VERSION@"; public static final File DATA_DIR=new File(MinecraftUtil.getMinecraftDataDir(),"CustomSkinLoader"), LOG_FILE=new File(DATA_DIR,"CustomSkinLoader.log"), CONFIG_FILE=new File(DATA_DIR,"CustomSkinLoader.json"); public static final SkinSiteProfile[] DEFAULT_LOAD_LIST={ SkinSiteProfile.createMojangAPI("Mojang"), SkinSiteProfile.createCustomSkinAPI("LittleSkin","https://littlesk.in/csl/"), SkinSiteProfile.createCustomSkinAPI("BlessingSkin","http://skin.prinzeugen.net/"), //OneSkin has been removed temporarily //SkinSiteProfile.createCustomSkinAPI("OneSkin","http://fleey.cn/skin/skin_user/skin_json.php/"), //Minecrack could not load skin correctly //SkinSiteProfile.creatLegacy("Minecrack","http://minecrack.fr.nf/mc/skinsminecrackd/{USERNAME}.png","http://minecrack.fr.nf/mc/cloaksminecrackd/{USERNAME}.png",null), SkinSiteProfile.createElyByAPI("ElyBy"), SkinSiteProfile.createUniSkinAPI("SkinMe","http://www.skinme.cc/uniskin/"), SkinSiteProfile.createLegacy("LocalSkin","LocalSkin/skins/{USERNAME}.png","LocalSkin/capes/{USERNAME}.png","LocalSkin/elytras/{USERNAME}.png"), SkinSiteProfile.createGlitchlessAPI("GlitchlessGames","https://games.glitchless.ru/api/minecraft/users/profiles/textures/?nickname=")}; public static final Gson GSON=new GsonBuilder().setPrettyPrinting().create(); public static final Logger logger=initLogger(); public static final Config config=Config.loadConfig0(); private static final ProfileCache profileCache=new ProfileCache(); private static final DynamicSkullManager dynamicSkullManager=new DynamicSkullManager(); //Correct thread name in thread pool private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); private static final ThreadFactory customFactory = r -> { Thread t = defaultFactory.newThread(r); if(r instanceof Thread) { t.setName(((Thread) r).getName()); } return t; }; //Thread pool will discard oldest task when queue reaches 333 tasks private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( config.threadPoolSize, config.threadPoolSize, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue<>(333), customFactory, new ThreadPoolExecutor.DiscardOldestPolicy() ); //For User Skin public static Map loadProfile(GameProfile gameProfile){ String username=gameProfile.getName(); String credential=MinecraftUtil.getCredential(gameProfile); //Fix: http://hopper.minecraft.net/crashes/minecraft/MCX-2773713 if(username==null){ logger.warning("Could not load profile: username is null."); return Maps.newHashMap(); } String tempName=Thread.currentThread().getName(); Thread.currentThread().setName(username);//Change Thread Name UserProfile profile; if(profileCache.isReady(credential)){ logger.info("Cached profile will be used."); profile=profileCache.getProfile(credential); if(profile==null){ logger.warning("(!Cached Profile is empty!) Expiry:"+profileCache.getExpiry(credential)); if(profileCache.isExpired(credential))//force load profile=loadProfile0(gameProfile); } else logger.info(profile.toString(profileCache.getExpiry(credential))); }else{ profileCache.setLoading(credential, true); profile=loadProfile0(gameProfile); } Thread.currentThread().setName(tempName); return ModelManager0.fromUserProfile(profile); } //Core public static UserProfile loadProfile0(GameProfile gameProfile){ String username=gameProfile.getName(); String credential=MinecraftUtil.getCredential(gameProfile); profileCache.setLoading(credential, true); logger.info("Loading "+username+"'s profile."); if(config.loadlist==null||config.loadlist.isEmpty()){ logger.info("LoadList is Empty."); return null; } UserProfile profile0=new UserProfile(); for(int i=0;i loadProfileFromCache(final GameProfile gameProfile) { String username=gameProfile.getName(); String credential=MinecraftUtil.getCredential(gameProfile); if(username == null || credential == null) return dynamicSkullManager.getTexture(gameProfile); if(config.forceUpdateSkull ?profileCache.isReady(credential):profileCache.isExist(credential)){ UserProfile profile=profileCache.getProfile(credential); return ModelManager0.fromUserProfile(profile); } if (!profileCache.isLoading(credential)) { profileCache.setLoading(credential, true); Runnable loadThread = () -> { String tempName = Thread.currentThread().getName(); Thread.currentThread().setName(username + "'s skull"); loadProfile0(gameProfile);//Load in thread Thread.currentThread().setName(tempName); }; if (config.forceUpdateSkull) { new Thread(loadThread).start(); } else { threadPool.execute(loadThread); } } return Maps.newHashMap(); } private static Logger initLogger() { Logger logger=new Logger(LOG_FILE); logger.info("CustomSkinLoader "+CustomSkinLoader_FULL_VERSION); logger.info("DataDir: "+DATA_DIR.getAbsolutePath()); logger.info("Minecraft: "+MinecraftUtil.getMinecraftMainVersion()+"("+MinecraftUtil.getMinecraftVersionText()+")"); return logger; } }