From f64af50a072f2d59d66500e6ad5d82fa660e8d5d Mon Sep 17 00:00:00 2001 From: ZekerZhayard Date: Sun, 16 Aug 2020 19:50:19 +0800 Subject: [PATCH] Improve downloading http textures. (#108) --- .../customskinloader/config/Config.java | 2 + .../fake/FakeSkinManager.java | 23 ++-- .../fake/FakeThreadDownloadImageData.java | 101 ++++++++++++++++++ .../utils/HttpRequestUtil.java | 4 +- .../customskinloader/utils/MinecraftUtil.java | 42 -------- 5 files changed, 119 insertions(+), 53 deletions(-) create mode 100644 Common/source/customskinloader/fake/FakeThreadDownloadImageData.java diff --git a/Common/source/customskinloader/config/Config.java b/Common/source/customskinloader/config/Config.java index 8999f7d..af69b8d 100644 --- a/Common/source/customskinloader/config/Config.java +++ b/Common/source/customskinloader/config/Config.java @@ -28,6 +28,7 @@ public class Config { public boolean forceLoadAllTextures=false; public boolean enableCape = true; public int threadPoolSize = 1; + public int retryTime = 3; //Cache public int cacheExpiry=30; @@ -58,6 +59,7 @@ public class Config { config.loadExtraList(); config.initLocalFolder(); config.threadPoolSize = Math.max(config.threadPoolSize, 1); + config.retryTime = Math.max(config.retryTime, 0); if(config.ignoreHttpsCertificate) HttpUtil0.ignoreHttpsCertificate(); if(config.enableCacheAutoClean && !config.enableLocalProfileCache){ diff --git a/Common/source/customskinloader/fake/FakeSkinManager.java b/Common/source/customskinloader/fake/FakeSkinManager.java index 97e2db6..5a12829 100644 --- a/Common/source/customskinloader/fake/FakeSkinManager.java +++ b/Common/source/customskinloader/fake/FakeSkinManager.java @@ -20,7 +20,6 @@ import customskinloader.fake.itf.IFakeTextureManager_1; import customskinloader.fake.itf.IFakeTextureManager_2; import customskinloader.utils.HttpTextureUtil; import customskinloader.utils.HttpTextureUtil.HttpTextureInfo; -import customskinloader.utils.MinecraftUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.texture.SimpleTexture; @@ -46,14 +45,16 @@ public class FakeSkinManager { } public ResourceLocation loadSkin(final MinecraftProfileTexture profileTexture, final Type textureType, final SkinAvailableCallback skinAvailableCallback) { - HttpTextureInfo info = HttpTextureUtil.toHttpTextureInfo(profileTexture.getUrl()); + return this.loadSkin(profileTexture, HttpTextureUtil.toHttpTextureInfo(profileTexture.getUrl()), textureType, skinAvailableCallback); + } + private ResourceLocation loadSkin(final MinecraftProfileTexture profileTexture, final HttpTextureInfo info, final Type textureType, final SkinAvailableCallback skinAvailableCallback) { final ResourceLocation resourcelocation = new ResourceLocation("skins/" + info.hash); if (((IFakeTextureManager_1) this.textureManager).func_229267_b_(resourcelocation) != null) {//Have already loaded makeCallback(skinAvailableCallback, textureType, resourcelocation, modelCache.getOrDefault(resourcelocation, profileTexture)); } else { - SimpleTexture threaddownloadimagedata = MinecraftUtil.createThreadDownloadImageData(info.cacheFile, info.url, DefaultPlayerSkin.getDefaultSkinLegacy(), new BaseBuffer(skinAvailableCallback, textureType, resourcelocation, profileTexture), textureType); + SimpleTexture threaddownloadimagedata = FakeThreadDownloadImageData.createThreadDownloadImageData(info.cacheFile, info.url, DefaultPlayerSkin.getDefaultSkinLegacy(), new BaseBuffer(skinAvailableCallback, textureType, resourcelocation, profileTexture), textureType); if (skinAvailableCallback instanceof FakeClientPlayer.LegacyBuffer)//Cache for client player FakeClientPlayer.textureCache.put(resourcelocation, threaddownloadimagedata); ((IFakeTextureManager_2) this.textureManager).func_229263_a_(resourcelocation, (Texture) threaddownloadimagedata); @@ -67,14 +68,18 @@ public class FakeSkinManager { final Map map = Maps.newHashMap(); map.putAll(customskinloader.CustomSkinLoader.loadProfile(profile)); - ((IFakeMinecraft) Minecraft.getMinecraft()).execute(() -> { - for (Type type : Type.values()) { - if (map.containsKey(type)) { + for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.Type.values()) { + MinecraftProfileTexture profileTexture = map.get(type); + if (profileTexture != null) { + HttpTextureInfo info = HttpTextureUtil.toHttpTextureInfo(profileTexture.getUrl()); + FakeThreadDownloadImageData.downloadTexture(info.cacheFile, info.url); + + ((IFakeMinecraft) Minecraft.getMinecraft()).execute(() -> { CustomSkinLoader.logger.debug("Loading type:" + type); - FakeSkinManager.this.loadSkin(map.get(type), type, skinAvailableCallback); - } + FakeSkinManager.this.loadSkin(profileTexture, info, type, skinAvailableCallback); + }); } - }); + } } }); } diff --git a/Common/source/customskinloader/fake/FakeThreadDownloadImageData.java b/Common/source/customskinloader/fake/FakeThreadDownloadImageData.java new file mode 100644 index 0000000..087ff1e --- /dev/null +++ b/Common/source/customskinloader/fake/FakeThreadDownloadImageData.java @@ -0,0 +1,101 @@ +package customskinloader.fake; + +import java.io.File; +import java.util.EnumMap; +import java.util.function.Supplier; + +import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import customskinloader.CustomSkinLoader; +import customskinloader.utils.HttpRequestUtil; +import net.minecraft.client.renderer.IImageBuffer; +import net.minecraft.client.renderer.ThreadDownloadImageData; +import net.minecraft.client.renderer.texture.DownloadingTexture; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.util.ResourceLocation; + +public class FakeThreadDownloadImageData { + private static IThreadDownloadImageDataBuilder builder; + + public static SimpleTexture createThreadDownloadImageData(File cacheFileIn, String imageUrlIn, ResourceLocation textureResourceLocationIn, IImageBuffer imageBufferIn, MinecraftProfileTexture.Type textureTypeIn) { + SimpleTexture texture = null; + if (FakeThreadDownloadImageData.builder == null) { + EnumMap throwables = new EnumMap<>(ThreadDownloadImageDataBuilder.class); + for (ThreadDownloadImageDataBuilder builder : ThreadDownloadImageDataBuilder.values()) { + try { + FakeThreadDownloadImageData.builder = builder.get().get(); + texture = FakeThreadDownloadImageData.builder.build(cacheFileIn, imageUrlIn, textureResourceLocationIn, imageBufferIn, textureTypeIn); + CustomSkinLoader.logger.info("ThreadDownloadImageData Class: %s", texture.getClass().getName()); + break; + } catch (Throwable t) { + throwables.put(builder, t); + } + } + + if (texture == null) { + CustomSkinLoader.logger.warning("Unable to get ThreadDownloadImageData Class: "); + throwables.forEach((k, v) -> { + CustomSkinLoader.logger.warning("Caused by: (%s)", k.name()); + CustomSkinLoader.logger.warning(v); + }); + throw new RuntimeException("Unable to get ThreadDownloadImageData Class!"); + } + } else { + texture = builder.build(cacheFileIn, imageUrlIn, textureResourceLocationIn, imageBufferIn, textureTypeIn); + } + return texture; + } + + public static void downloadTexture(File cacheFile, String imageUrl) { + HttpRequestUtil.HttpRequest request = new HttpRequestUtil.HttpRequest(imageUrl).setLoadContent(false).setCacheTime(0).setCacheFile(cacheFile); + for (int i = 0; i <= CustomSkinLoader.config.retryTime; i++) { + if (i != 0) { + CustomSkinLoader.logger.debug("Retry to download texture %s (%s)", imageUrl, i); + } + if (HttpRequestUtil.makeHttpRequest(request).success) { + break; + } + } + } + + private interface IThreadDownloadImageDataBuilder { + SimpleTexture build(File cacheFile, String imageUrl, ResourceLocation textureResourceLocation, IImageBuffer imageBuffer, MinecraftProfileTexture.Type textureType); + } + + private enum ThreadDownloadImageDataBuilder { + // DO NOT replace new IThreadDownloadImageDataBuilder() with lambda. + V1(() -> new IThreadDownloadImageDataBuilder() { // Forge 1.8.x~1.12.x + @Override + public SimpleTexture build(File cacheFile, String imageUrl, ResourceLocation textureResourceLocation, IImageBuffer imageBuffer, MinecraftProfileTexture.Type textureType) { + return new ThreadDownloadImageData(cacheFile, imageUrl, textureResourceLocation, imageBuffer); + } + }), + V2(() -> new IThreadDownloadImageDataBuilder() { // Forge 1.13.x + @Override + public SimpleTexture build(File cacheFile, String imageUrl, ResourceLocation textureResourceLocation, IImageBuffer imageBuffer, MinecraftProfileTexture.Type textureType) { + return new net.minecraft.client.renderer.texture.ThreadDownloadImageData(cacheFile, imageUrl, textureResourceLocation, imageBuffer); + } + }), + V3(() -> new IThreadDownloadImageDataBuilder() { // Forge 1.14.x + @Override + public SimpleTexture build(File cacheFile, String imageUrl, ResourceLocation textureResourceLocation, IImageBuffer imageBuffer, MinecraftProfileTexture.Type textureType) { + return new DownloadingTexture(cacheFile, imageUrl, textureResourceLocation, imageBuffer); + } + }), + V4(() -> new IThreadDownloadImageDataBuilder() { // Forge 1.15.x~1.16.x + @Override + public SimpleTexture build(File cacheFile, String imageUrl, ResourceLocation textureResourceLocation, IImageBuffer imageBuffer, MinecraftProfileTexture.Type textureType) { + return new DownloadingTexture(cacheFile, imageUrl, textureResourceLocation, textureType == MinecraftProfileTexture.Type.SKIN, imageBuffer); + } + }); + + private final Supplier builder; + + ThreadDownloadImageDataBuilder(Supplier builder) { + this.builder = builder; + } + + public Supplier get() { + return this.builder; + } + } +} diff --git a/Common/source/customskinloader/utils/HttpRequestUtil.java b/Common/source/customskinloader/utils/HttpRequestUtil.java index 44720d9..246c2ab 100644 --- a/Common/source/customskinloader/utils/HttpRequestUtil.java +++ b/Common/source/customskinloader/utils/HttpRequestUtil.java @@ -208,7 +208,7 @@ public class HttpRequestUtil { return responce; } catch (Exception e) { - CustomSkinLoader.logger.debug("Failed to request (Exception: " + e.toString() + ")"); + CustomSkinLoader.logger.debug("Failed to request " + request.url + " (Exception: " + e.toString() + ")"); return loadFromCache(request, new HttpResponce()); } } @@ -224,7 +224,7 @@ public class HttpRequestUtil { private static HttpResponce loadFromCache(HttpRequest request, HttpResponce responce, long expireTime) { if (request.cacheFile == null || !request.cacheFile.isFile()) return responce; - CustomSkinLoader.logger.debug("Cache file found (Length: " + request.cacheFile.length() + " , Path: '" + request.cacheFile.getAbsolutePath() + "' , Expire: " + expireTime + "')"); + CustomSkinLoader.logger.debug("Cache file found (Length: " + request.cacheFile.length() + " , Path: '" + request.cacheFile.getAbsolutePath() + "' , Expire: " + expireTime + ")"); responce.fromCache = true; responce.success = true; if (!request.loadContent) diff --git a/Common/source/customskinloader/utils/MinecraftUtil.java b/Common/source/customskinloader/utils/MinecraftUtil.java index d0d5479..b499d79 100644 --- a/Common/source/customskinloader/utils/MinecraftUtil.java +++ b/Common/source/customskinloader/utils/MinecraftUtil.java @@ -1,7 +1,6 @@ package customskinloader.utils; import java.io.File; -import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; @@ -9,14 +8,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.IImageBuffer; -import net.minecraft.client.renderer.ThreadDownloadImageData; -import net.minecraft.client.renderer.texture.SimpleTexture; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.SkinManager; -import net.minecraft.util.ResourceLocation; import org.apache.commons.lang3.StringUtils; /** @@ -36,42 +30,6 @@ public class MinecraftUtil { return Minecraft.getMinecraft().getSkinManager(); } - private static boolean is_1_15_plus = false; - private static Constructor constructor = null; - public static SimpleTexture createThreadDownloadImageData(File cacheFileIn, String imageUrlIn, ResourceLocation textureResourceLocation, IImageBuffer imageBufferIn, MinecraftProfileTexture.Type textureType) { - if (constructor == null) { - Class c; - try { - c = Class.forName("net.minecraft.client.renderer.texture.ThreadDownloadImageData", false, MinecraftUtil.class.getClassLoader()); // Forge 1.13 - } catch (ClassNotFoundException e1) { - try { - c = Class.forName("net.minecraft.client.renderer.texture.DownloadingTexture", false, MinecraftUtil.class.getClassLoader()); // Forge 1.14+ - } catch (ClassNotFoundException e2) { - c = ThreadDownloadImageData.class; // Forge 1.12- or Fabric or Vanilla - } - } - try { - constructor = c.getConstructor(File.class, String.class, ResourceLocation.class, boolean.class, Runnable.class); // For 1.15+ - is_1_15_plus = true; - } catch (NoSuchMethodException e1) { - try { - constructor = c.getConstructor(File.class, String.class, ResourceLocation.class, IImageBuffer.class); // For 1.15- - } catch (NoSuchMethodException e2) { - throw new RuntimeException(e2); - } - } - } - try { - if (is_1_15_plus) { - return (SimpleTexture) constructor.newInstance(cacheFileIn, imageUrlIn, textureResourceLocation, textureType == MinecraftProfileTexture.Type.SKIN, imageBufferIn); - } else { - return (SimpleTexture) constructor.newInstance(cacheFileIn, imageUrlIn, textureResourceLocation, imageBufferIn); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - private static ArrayList minecraftVersion = new ArrayList(); private static String minecraftMainVersion = null;