Files

254 lines
11 KiB
Java
Raw Permalink Normal View History

2016-02-27 15:22:35 +08:00
package customskinloader;
import java.io.File;
2016-02-27 21:43:46 +08:00
import java.util.Map;
2022-07-28 09:24:44 +08:00
import java.util.concurrent.ExecutorService;
2021-04-24 10:33:18 +08:00
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
2021-09-24 11:19:06 +08:00
import java.util.function.Consumer;
2016-02-27 15:22:35 +08:00
2016-04-04 13:13:18 +08:00
import com.google.common.collect.Maps;
2016-02-27 15:22:35 +08:00
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
2016-07-31 22:00:46 +08:00
import com.mojang.authlib.GameProfile;
2016-04-03 01:14:33 +08:00
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
2016-02-27 15:22:35 +08:00
import customskinloader.config.Config;
import customskinloader.config.SkinSiteProfile;
2021-04-24 10:33:18 +08:00
import customskinloader.loader.ProfileLoader;
2023-01-26 18:51:20 +08:00
import customskinloader.log.LogManager;
import customskinloader.log.Logger;
2016-09-10 21:00:53 +08:00
import customskinloader.profile.DynamicSkullManager;
2016-07-31 22:00:46 +08:00
import customskinloader.profile.ModelManager0;
import customskinloader.profile.ProfileCache;
import customskinloader.profile.UserProfile;
2016-04-30 22:12:26 +08:00
import customskinloader.utils.MinecraftUtil;
2016-02-27 15:22:35 +08:00
/**
* Custom skin loader mod for Minecraft.
2022-07-29 00:07:04 +08:00
*
* @author Jeremy Lam [JLChnToZ] 2013-2014 & Alexander Xia [xfl03] 2014-2023
2016-08-20 19:36:44 +08:00
* @version @MOD_FULL_VERSION@
2016-02-27 15:22:35 +08:00
*/
2016-06-06 13:48:59 +08:00
public class CustomSkinLoader {
2022-07-29 00:07:04 +08:00
public static final String CustomSkinLoader_VERSION = "@MOD_VERSION@";
public static final String CustomSkinLoader_FULL_VERSION = "@MOD_FULL_VERSION@";
public static final int CustomSkinLoader_BUILD_NUMBER = Integer.parseInt("@MOD_BUILD_NUMBER@");
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 Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static final Logger logger = initLogger();
public static final Config config = initConfig();
2022-07-29 00:07:04 +08:00
private static final ProfileCache profileCache = new ProfileCache();
private static final DynamicSkullManager dynamicSkullManager = new DynamicSkullManager();
2020-03-24 11:46:11 +08:00
private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(
config.threadPoolSize, config.threadPoolSize, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
2022-07-28 09:24:44 +08:00
2020-08-16 21:55:06 +08:00
//Correct thread name in thread pool
private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
private static final ThreadFactory customFactory = r -> {
Thread t = defaultFactory.newThread(r);
2022-07-29 00:07:04 +08:00
if (r instanceof Thread) {
2020-08-16 21:55:06 +08:00
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()
);
2021-04-24 10:33:18 +08:00
2022-07-28 09:24:44 +08:00
public static void loadProfileTextures(Runnable runnable) {
THREAD_POOL.execute(runnable);
}
public static void loadProfileLazily(
GameProfile gameProfile, Consumer<Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> consumer) {
2021-09-24 11:19:06 +08:00
String username = gameProfile.getName();
String credential = MinecraftUtil.getCredential(gameProfile);
// Fix: http://hopper.minecraft.net/crashes/minecraft/MCX-2773713
if (username == null) {
2018-02-12 12:20:28 +08:00
logger.warning("Could not load profile: username is null.");
2021-09-24 11:19:06 +08:00
consumer.accept(Maps.newHashMap());
return;
2018-02-12 12:20:28 +08:00
}
2021-09-24 11:19:06 +08:00
String tempName = Thread.currentThread().getName();
Thread.currentThread().setName(username); // Change Thread Name
if (profileCache.isLoading(credential)) {
profileCache.putLoader(credential, consumer);
Thread.currentThread().setName(tempName);
return;
}
consumer.accept(loadProfile(gameProfile));
Thread.currentThread().setName(tempName);
profileCache.getLastLoader(credential).ifPresent(c -> loadProfileLazily(gameProfile, c));
}
//For User Skin
2022-07-29 00:07:04 +08:00
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadProfile(GameProfile gameProfile) {
2021-09-24 11:19:06 +08:00
String credential = MinecraftUtil.getCredential(gameProfile);
2018-06-25 00:34:32 +08:00
UserProfile profile;
2021-09-24 11:19:06 +08:00
if (profileCache.isReady(credential)) {
2018-02-12 12:20:28 +08:00
logger.info("Cached profile will be used.");
2021-09-24 11:19:06 +08:00
profile = profileCache.getProfile(credential);
if (profile == null) {
logger.warning("(Cached Profile is empty!) Expiry: " + profileCache.getExpiry(credential));
2022-07-29 00:07:04 +08:00
if (profileCache.isExpired(credential)) { // force load
2021-09-24 11:19:06 +08:00
profile = loadProfile0(gameProfile, false);
2022-07-29 00:07:04 +08:00
}
} else {
2018-02-12 12:20:28 +08:00
logger.info(profile.toString(profileCache.getExpiry(credential)));
2022-07-29 00:07:04 +08:00
}
2021-09-24 11:19:06 +08:00
} else {
2018-02-12 12:20:28 +08:00
profileCache.setLoading(credential, true);
2021-09-24 11:19:06 +08:00
profile = loadProfile0(gameProfile, false);
2018-02-12 12:20:28 +08:00
}
return ModelManager0.fromUserProfile(profile);
}
2022-07-29 00:07:04 +08:00
2018-02-12 12:20:28 +08:00
//Core
2022-07-29 00:07:04 +08:00
public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) {
String username = gameProfile.getName();
String credential = MinecraftUtil.getCredential(gameProfile);
2018-02-12 12:20:28 +08:00
profileCache.setLoading(credential, true);
2022-07-29 00:07:04 +08:00
logger.info("Loading " + username + "'s profile.");
if (config.loadlist == null || config.loadlist.isEmpty()) {
2018-02-12 12:20:28 +08:00
logger.info("LoadList is Empty.");
return null;
}
2022-07-29 00:07:04 +08:00
UserProfile profile0 = new UserProfile();
for (int i = 0; i < config.loadlist.size(); i++) {
SkinSiteProfile ssp = config.loadlist.get(i);
logger.info((i + 1) + "/" + config.loadlist.size() + " Try to load profile from '" + ssp.name + "'.");
if (ssp.type == null) {
2021-11-28 02:17:38 +08:00
logger.info("The type of '" + ssp.name + "' is null.");
continue;
}
2022-07-29 00:07:04 +08:00
ProfileLoader.IProfileLoader loader = ProfileLoader.LOADERS.get(ssp.type.toLowerCase());
if (loader == null) {
logger.info("Type '" + ssp.type + "' is not defined.");
2018-02-12 12:20:28 +08:00
continue;
}
2022-07-29 00:07:04 +08:00
UserProfile profile = null;
try {
profile = loader.loadProfile(ssp, gameProfile);
} catch (Exception e) {
2018-02-12 12:20:28 +08:00
logger.warning("Exception occurs while loading.");
logger.warning(e);
2022-07-29 00:07:04 +08:00
if (e.getCause() != null) {
2018-02-12 12:20:28 +08:00
logger.warning("Caused By:");
logger.warning(e.getCause());
}
}
2022-07-29 00:07:04 +08:00
if (profile == null) {
2018-02-12 12:20:28 +08:00
continue;
2022-07-29 00:07:04 +08:00
}
2018-02-12 12:20:28 +08:00
profile0.mix(profile);
2022-07-29 00:07:04 +08:00
if (isSkull && !profile0.hasSkinUrl()) {
2021-09-24 11:19:06 +08:00
continue;
2022-07-29 00:07:04 +08:00
}
if (!config.forceLoadAllTextures) {
2021-09-24 11:19:06 +08:00
break;
2022-07-29 00:07:04 +08:00
}
if (profile0.isFull()) {
2018-02-12 12:20:28 +08:00
break;
2022-07-29 00:07:04 +08:00
}
2018-02-12 12:20:28 +08:00
}
2022-07-29 00:07:04 +08:00
if (!profile0.isEmpty()) {
logger.info(username + "'s profile loaded.");
if (!config.enableCape) {
2018-11-26 15:16:07 +08:00
profile0.capeUrl = null;
2022-07-29 00:07:04 +08:00
}
2018-02-12 12:20:28 +08:00
profileCache.updateCache(credential, profile0);
profileCache.setLoading(credential, false);
logger.info(profile0.toString(profileCache.getExpiry(credential)));
return profile0;
}
2022-07-29 00:07:04 +08:00
logger.info(username + "'s profile not found in load list.");
if (config.enableLocalProfileCache) {
UserProfile profile = profileCache.getLocalProfile(credential);
if (profile == null) {
logger.info(username + "'s LocalProfile not found.");
} else {
2018-02-12 12:20:28 +08:00
profileCache.updateCache(credential, profile, false);
profileCache.setLoading(credential, false);
2022-07-29 00:07:04 +08:00
logger.info(username + "'s LocalProfile will be used.");
2018-02-12 12:20:28 +08:00
logger.info(profile.toString(profileCache.getExpiry(credential)));
return profile;
}
}
profileCache.setLoading(credential, false);
return null;
}
2022-07-29 00:07:04 +08:00
2018-02-12 12:20:28 +08:00
//For Skull
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadProfileFromCache(
final GameProfile gameProfile) {
2022-07-29 00:07:04 +08:00
String username = gameProfile.getName();
String credential = MinecraftUtil.getCredential(gameProfile);
//CustomSkinLoader needs username to load standard skin, if username not exist, only textures in NBT can be used
//Authlib 3.11.50 makes empty username to " "
if (username == null || username.isEmpty() || username.equals(" ") || credential == null) {
2018-02-12 12:20:28 +08:00
return dynamicSkullManager.getTexture(gameProfile);
2022-07-29 00:07:04 +08:00
}
if (config.forceUpdateSkull ? profileCache.isReady(credential) : profileCache.isExist(credential)) {
UserProfile profile = profileCache.getProfile(credential);
2018-02-12 12:20:28 +08:00
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");
2021-09-24 11:19:06 +08:00
loadProfile0(gameProfile, true);//Load in thread
Thread.currentThread().setName(tempName);
};
2020-08-16 21:07:22 +08:00
if (config.forceUpdateSkull) {
new Thread(loadThread).start();
} else {
threadPool.execute(loadThread);
}
}
2018-02-12 12:20:28 +08:00
return Maps.newHashMap();
}
2022-07-29 00:07:04 +08:00
2018-02-12 12:20:28 +08:00
private static Logger initLogger() {
2023-01-26 18:51:20 +08:00
LogManager.setLogFile(LOG_FILE.toPath());
Logger logger = LogManager.getLogger("Core");
logger.info("CustomSkinLoader " + CustomSkinLoader_FULL_VERSION);
logger.info("DataDir: " + DATA_DIR.getAbsolutePath());
logger.info("Operating System: " + System.getProperty("os.name") +
" (" + System.getProperty("os.arch") + ") version " + System.getProperty("os.version"));
logger.info("Java Version: " + System.getProperty("java.version") +
", " + System.getProperty("java.vendor"));
logger.info("Java VM Version: " + System.getProperty("java.vm.name") +
" (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
2022-07-27 17:23:50 +08:00
logger.info("Minecraft: " + MinecraftUtil.getMinecraftMainVersion());
2018-02-12 12:20:28 +08:00
return logger;
}
/**
* Create <code>Config</code> object.
* To use <code>logger</code>, please make sure call this function after <code>initLogger</code>.
*
* @return <code>Config</code>
*/
private static Config initConfig() {
Config config = Config.loadConfig0();
logger.enableLogStdOut = config.enableLogStdOut;
return config;
}
2016-02-27 15:22:35 +08:00
}