generated from Legacy-Fabric/fabric-example-mod
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
59 changed files
with
5,213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package customskinloader; | ||
|
||
import java.io.File; | ||
import java.util.Map; | ||
import java.util.concurrent.ExecutorService; | ||
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; | ||
import java.util.function.Consumer; | ||
|
||
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 customskinloader.config.Config; | ||
import customskinloader.config.SkinSiteProfile; | ||
import customskinloader.loader.ProfileLoader; | ||
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-2022 | ||
* @version @MOD_FULL_VERSION@ | ||
*/ | ||
public class CustomSkinLoader { | ||
public static final String CustomSkinLoader_VERSION = "14.14-LF"; | ||
public static final String CustomSkinLoader_FULL_VERSION = "14.14-LF"; | ||
public static final int CustomSkinLoader_BUILD_NUMBER = 0; | ||
|
||
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 = Config.loadConfig0(); | ||
|
||
private static final ProfileCache profileCache = new ProfileCache(); | ||
private static final DynamicSkullManager dynamicSkullManager = new DynamicSkullManager(); | ||
|
||
private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(config.threadPoolSize, config.threadPoolSize, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue<>()); | ||
|
||
//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 the 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() | ||
); | ||
|
||
public static void loadProfileTextures(Runnable runnable) { | ||
THREAD_POOL.execute(runnable); | ||
} | ||
|
||
public static void loadProfileLazily(GameProfile gameProfile, Consumer<Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> consumer) { | ||
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."); | ||
consumer.accept(Maps.newHashMap()); | ||
return; | ||
} | ||
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 | ||
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadProfile(GameProfile gameProfile) { | ||
String credential = MinecraftUtil.getCredential(gameProfile); | ||
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, false); | ||
} | ||
} else { | ||
logger.info(profile.toString(profileCache.getExpiry(credential))); | ||
} | ||
} else { | ||
profileCache.setLoading(credential, true); | ||
profile = loadProfile0(gameProfile, false); | ||
} | ||
return ModelManager0.fromUserProfile(profile); | ||
} | ||
|
||
//Core | ||
public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) { | ||
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 < 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) { | ||
logger.info("The type of '" + ssp.name + "' is null."); | ||
continue; | ||
} | ||
ProfileLoader.IProfileLoader loader = ProfileLoader.LOADERS.get(ssp.type.toLowerCase()); | ||
if (loader == null) { | ||
logger.info("Type '" + ssp.type + "' is not defined."); | ||
continue; | ||
} | ||
UserProfile profile = null; | ||
try { | ||
profile = loader.loadProfile(ssp, gameProfile); | ||
} catch (Exception e) { | ||
logger.warning("Exception occurs while loading."); | ||
logger.warning(e); | ||
if (e.getCause() != null) { | ||
logger.warning("Caused By:"); | ||
logger.warning(e.getCause()); | ||
} | ||
} | ||
if (profile == null) { | ||
continue; | ||
} | ||
profile0.mix(profile); | ||
if (isSkull && !profile0.hasSkinUrl()) { | ||
continue; | ||
} | ||
if (!config.forceLoadAllTextures) { | ||
break; | ||
} | ||
if (profile0.isFull()) { | ||
break; | ||
} | ||
} | ||
if (!profile0.isEmpty()) { | ||
logger.info(username + "'s profile loaded."); | ||
if (!config.enableCape) { | ||
profile0.capeUrl = null; | ||
} | ||
profileCache.updateCache(credential, profile0); | ||
profileCache.setLoading(credential, false); | ||
logger.info(profile0.toString(profileCache.getExpiry(credential))); | ||
return profile0; | ||
} | ||
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 { | ||
profileCache.updateCache(credential, profile, false); | ||
profileCache.setLoading(credential, false); | ||
logger.info(username + "'s LocalProfile will be used."); | ||
logger.info(profile.toString(profileCache.getExpiry(credential))); | ||
return profile; | ||
} | ||
} | ||
profileCache.setLoading(credential, false); | ||
return null; | ||
} | ||
|
||
//For Skull | ||
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadProfileFromCache(final GameProfile gameProfile) { | ||
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 "\u0020" | ||
if (username == null || username.isEmpty() || username.equals("\u0020") || 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, true);//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("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")); | ||
logger.info("Minecraft: " + MinecraftUtil.getMinecraftMainVersion() + "Legacy Fabric"); | ||
return logger; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package customskinloader; | ||
|
||
import java.io.BufferedWriter; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.OutputStreamWriter; | ||
import java.io.PrintWriter; | ||
import java.io.StringWriter; | ||
import java.text.SimpleDateFormat; | ||
import java.util.Date; | ||
|
||
@SuppressWarnings("ResultOfMethodCallIgnored") | ||
public class Logger { | ||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | ||
public enum Level{ | ||
DEBUG("DEBUG",false), | ||
INFO("INFO",true), | ||
WARNING("WARNING",true); | ||
|
||
|
||
String name; | ||
boolean display; | ||
Level(String name,boolean display){ | ||
this.name=name; | ||
this.display=display; | ||
} | ||
public String getName(){ | ||
return name; | ||
} | ||
public boolean display(){ | ||
return display; | ||
} | ||
} | ||
private BufferedWriter writer=null; | ||
public Logger(){ | ||
//Logger isn't created. | ||
} | ||
public Logger(String logFile) { | ||
this(new File(logFile)); | ||
} | ||
public Logger(File logFile){ | ||
try { | ||
if(!logFile.getParentFile().exists()){ | ||
logFile.getParentFile().mkdirs(); | ||
} | ||
if(!logFile.exists()) | ||
logFile.createNewFile(); | ||
|
||
writer = new BufferedWriter(new OutputStreamWriter( | ||
new FileOutputStream(logFile), "UTF-8")); | ||
|
||
System.out.println("Log Path: " + logFile.getAbsolutePath()); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
public void close(){ | ||
if(writer!=null){ | ||
try { | ||
writer.close(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} | ||
|
||
public void log(Level level,String msg){ | ||
if(!level.display()&&writer==null) | ||
return; | ||
String sb = String.format("[%s %s] %s", Thread.currentThread().getName(), level.getName(), msg); | ||
if(level.display()) | ||
System.out.println(sb); | ||
if(writer==null) | ||
return; | ||
try { | ||
String sb2 = String.format("[%s] %s\r\n", DATE_FORMAT.format(new Date()), sb); | ||
writer.write(sb2); | ||
writer.flush(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
public void debug(String msg){ | ||
log(Level.DEBUG,msg); | ||
} | ||
public void debug(String format, Object... objs) { | ||
debug(String.format(format, objs)); | ||
} | ||
public void info(String msg){ | ||
log(Level.INFO,msg); | ||
} | ||
public void info(String format, Object... objs){ | ||
info(String.format(format, objs)); | ||
} | ||
public void warning(String msg){ | ||
log(Level.WARNING,msg); | ||
} | ||
public void warning(String format, Object... objs){ | ||
warning(String.format(format, objs)); | ||
} | ||
public void warning(Throwable e){ | ||
StringWriter sw = new StringWriter(); | ||
e.printStackTrace(new PrintWriter(sw)); | ||
log(Level.WARNING,"Exception: "+sw.toString()); | ||
} | ||
} |
Oops, something went wrong.