diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinThread.java b/src/hdskins/java/com/voxelmodpack/hdskins/resource/ImageLoader.java similarity index 56% rename from src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinThread.java rename to src/hdskins/java/com/voxelmodpack/hdskins/resource/ImageLoader.java index 1d5e55fa..bd449663 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinThread.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/resource/ImageLoader.java @@ -3,6 +3,7 @@ package com.voxelmodpack.hdskins.resource; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.Callable; import javax.annotation.Nullable; @@ -15,20 +16,28 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.util.ResourceLocation; -public class SkinThread implements Runnable { +public class ImageLoader implements Callable { - private ResourceLocation original; - private ResourceLocation updated; - private BufferedImage image; + private Minecraft mc = Minecraft.getMinecraft(); - public SkinThread(ResourceLocation loc) { + private final ResourceLocation original; + + public ImageLoader(ResourceLocation loc) { this.original = loc; - new Thread(this).start(); } @Override - public void run() { - image = new ImageBufferDownloadHD().parseUserSkin(getImage(original)); + public ResourceLocation call() throws Exception { + BufferedImage image = getImage(original); + final BufferedImage updated = new ImageBufferDownloadHD().parseUserSkin(image); + return this.mc.addScheduledTask(new Callable() { + + @Override + public ResourceLocation call() throws Exception { + return loadSkin(updated); + } + + }).get(); } @Nullable @@ -46,24 +55,11 @@ public class SkinThread implements Runnable { return null; } - public ResourceLocation getResource() { - return this.updated; - } - - public void deleteTexture() { - Minecraft.getMinecraft().getTextureManager().deleteTexture(updated); - } - - public boolean isReady() { - return image != null; - } - - public void uploadSkin() { + private ResourceLocation loadSkin(BufferedImage image) { ResourceLocation conv = new ResourceLocation("hdskins-converted", original.getResourcePath()); - Minecraft.getMinecraft().getTextureManager().loadTexture(conv, new DynamicTextureImage(image)); - updated = conv; - - image = null; + this.mc.getTextureManager().loadTexture(conv, new DynamicTextureImage(image)); + return conv; } + } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinResourceManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinResourceManager.java index 29c0f7b6..8154212f 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinResourceManager.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/resource/SkinResourceManager.java @@ -5,18 +5,25 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.Gson; import com.google.gson.JsonParseException; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type; import com.mumfrey.liteloader.util.log.LiteLoaderLogger; +import net.minecraft.client.Minecraft; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManagerReloadListener; @@ -24,17 +31,24 @@ import net.minecraft.util.ResourceLocation; public class SkinResourceManager implements IResourceManagerReloadListener { + private ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + private Map uuidSkins = Maps.newHashMap(); private Map namedSkins = Maps.newHashMap(); - private Map converted = Maps.newHashMap(); + private Map> inProgress = Maps.newHashMap(); + private Map converted = Maps.newHashMap(); @Override public void onResourceManagerReload(IResourceManager resourceManager) { try { uuidSkins.clear(); namedSkins.clear(); - for (SkinThread loc : converted.values()) { - loc.deleteTexture(); + for (Future loc : inProgress.values()) { + loc.cancel(true); + } + inProgress.clear(); + for (ResourceLocation res : converted.values()) { + Minecraft.getMinecraft().getTextureManager().deleteTexture(res); } converted.clear(); for (IResource res : resourceManager.getAllResources(new ResourceLocation("hdskins", "textures/skins/skins.json"))) { @@ -74,19 +88,27 @@ public class SkinResourceManager implements IResourceManagerReloadListener { Skin skin = getSkin(profile); if (skin != null) { - ResourceLocation res = skin.getTexture(); + final ResourceLocation res = skin.getTexture(); if (res != null) { - SkinThread conv = this.converted.get(res); - if (conv == null) { + if (this.inProgress.get(res) == null) { // read and convert in a new thread - this.converted.put(res, conv = new SkinThread(res)); + final ListenableFuture conv = executor.submit(new ImageLoader(res)); + conv.addListener(new Runnable() { + @Override + public void run() { + try { + converted.put(res, conv.get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + }, executor); + this.inProgress.put(res, conv); } - // gotta stay in this thread to load it - if (conv.isReady()) { - conv.uploadSkin(); - } - return conv.getResource(); } + return converted.get(res); } return null; }