Format this to better convery what it's _trying_ to do. Killjoy pls

This commit is contained in:
Sollace 2018-08-10 12:28:18 +02:00
parent 0e530d9572
commit cddca0e1ea
6 changed files with 170 additions and 158 deletions

View file

@ -26,7 +26,6 @@ import com.voxelmodpack.hdskins.skins.SkinServer;
import com.voxelmodpack.hdskins.skins.ValhallaSkinServer; import com.voxelmodpack.hdskins.skins.ValhallaSkinServer;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.IImageBuffer;
import net.minecraft.client.renderer.texture.ITextureObject;
import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManager;
@ -88,12 +87,10 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
private List<ISkinModifier> skinModifiers = Lists.newArrayList(); private List<ISkinModifier> skinModifiers = Lists.newArrayList();
private SkinResourceManager resources = new SkinResourceManager(); private SkinResourceManager resources = new SkinResourceManager();
// private ExecutorService executor = Executors.newCachedThreadPool();
private Class<? extends GuiSkins> skinsClass = null; private Class<? extends GuiSkins> skinsClass = null;
private HDSkinManager() { private HDSkinManager() {
// register default skin server types // register default skin server types
addSkinServerType(LegacySkinServer.class); addSkinServerType(LegacySkinServer.class);
addSkinServerType(ValhallaSkinServer.class); addSkinServerType(ValhallaSkinServer.class);
@ -173,46 +170,40 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
return url + (url.indexOf('?') > -1 ? '&' : '?') + Long.toString(new Date().getTime() / 1000); return url + (url.indexOf('?') > -1 ? '&' : '?') + Long.toString(new Date().getTime() / 1000);
} }
private void loadTexture(GameProfile profile, final Type type, final SkinAvailableCallback callback) { private void loadTexture(GameProfile profile, Type type, SkinAvailableCallback callback) {
if (profile.getId() != null) { if (profile.getId() == null) {
Map<Type, MinecraftProfileTexture> data = getProfileData(profile); return;
final MinecraftProfileTexture texture = data.get(type); }
MinecraftProfileTexture texture = getProfileData(profile).get(type);
String skinDir = type.toString().toLowerCase() + "s/"; String skinDir = type.toString().toLowerCase() + "s/";
final ResourceLocation skin = new ResourceLocation("hdskins", skinDir + texture.getHash());
File file2 = new File(LiteLoader.getAssetsDirectory(), "hd/" + skinDir + texture.getHash().substring(0, 2) + "/" + texture.getHash());
final IImageBuffer imagebufferdownload = type == Type.SKIN ? new ImageBufferDownloadHD() : null; ResourceLocation skin = new ResourceLocation("hdskins", skinDir + texture.getHash());
ITextureObject texObject = new ThreadDownloadImageETag(file2, bustCache(texture.getUrl()), File etag = new File(LiteLoader.getAssetsDirectory(), "hd/" + skinDir + texture.getHash().substring(0, 2) + "/" + texture.getHash());
DefaultPlayerSkin.getDefaultSkinLegacy(),
new IImageBuffer() { IImageBuffer buffer = new ImageBufferDownloadHD() {
@Nonnull @Nonnull
@Override @Override
public BufferedImage parseUserSkin(@Nonnull BufferedImage image) { public BufferedImage parseUserSkin(@Nonnull BufferedImage image) {
BufferedImage image1 = image; if (type != Type.SKIN) {
if (imagebufferdownload != null) { return image;
image1 = imagebufferdownload.parseUserSkin(image);
}
return image1 == null ? image : image1;
} }
@Override BufferedImage converted = super.parseUserSkin(image);
public void skinAvailable() {
if (imagebufferdownload != null) { return converted == null ? image : converted;
imagebufferdownload.skinAvailable();
} }
callback.skinAvailable(type, skin, texture); }.withCallback(() -> callback.skinAvailable(type, skin, texture));
}
});
// schedule texture loading on the main thread. // schedule texture loading on the main thread.
TextureLoader.loadTexture(skin, texObject); TextureLoader.loadTexture(skin, new ThreadDownloadImageETag(etag, bustCache(texture.getUrl()), DefaultPlayerSkin.getDefaultSkinLegacy(), buffer));
}
} }
private Map<Type, MinecraftProfileTexture> loadProfileData(GameProfile profile) { private Map<Type, MinecraftProfileTexture> loadProfileData(GameProfile profile) {
Map<Type, MinecraftProfileTexture> textures = Maps.newEnumMap(Type.class); Map<Type, MinecraftProfileTexture> textures = Maps.newEnumMap(Type.class);
for (SkinServer server : skinServers) { for (SkinServer server : skinServers) {
try { try {
server.loadProfileData(profile).getTextures().forEach(textures::putIfAbsent); server.loadProfileData(profile).getTextures().forEach(textures::putIfAbsent);
@ -222,43 +213,47 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
} catch (IOException e) { } catch (IOException e) {
LogManager.getLogger().trace(e); LogManager.getLogger().trace(e);
} }
} }
return textures; return textures;
} }
public Map<Type, MinecraftProfileTexture> getProfileData(GameProfile profile) { public Map<Type, MinecraftProfileTexture> getProfileData(GameProfile profile) {
boolean was = !skins.asMap().containsKey(profile); boolean was = !skins.asMap().containsKey(profile);
Map<Type, MinecraftProfileTexture> textures = skins.getUnchecked(profile); Map<Type, MinecraftProfileTexture> textures = skins.getUnchecked(profile);
// This is the initial value. Refreshing will load it asynchronously. // This is the initial value. Refreshing will load it asynchronously.
if (was) { if (was) {
skins.refresh(profile); skins.refresh(profile);
} }
return textures; return textures;
} }
public void addSkinServerType(Class<? extends SkinServer> type) { public void addSkinServerType(Class<? extends SkinServer> type) {
Preconditions.checkArgument(!type.isInterface(), "type cannot be an interface"); Preconditions.checkArgument(!type.isInterface(), "type cannot be an interface");
Preconditions.checkArgument(!Modifier.isAbstract(type.getModifiers()), "type cannot be abstract"); Preconditions.checkArgument(!Modifier.isAbstract(type.getModifiers()), "type cannot be abstract");
ServerType st = type.getAnnotation(ServerType.class); ServerType st = type.getAnnotation(ServerType.class);
if (st == null) {
throw new IllegalArgumentException("class is not annotated with @ServerType"); Preconditions.checkArgument(st != null, "class is not annotated with @ServerType");
}
this.skinServerTypes.put(st.value(), type); skinServerTypes.put(st.value(), type);
} }
public Class<? extends SkinServer> getSkinServerClass(String type) { public Class<? extends SkinServer> getSkinServerClass(String type) {
return this.skinServerTypes.get(type); return skinServerTypes.get(type);
} }
public void addSkinServer(SkinServer skinServer) { public void addSkinServer(SkinServer skinServer) {
this.skinServers.add(skinServer); skinServers.add(skinServer);
} }
// TODO: Why is this deprecated? // TODO: Why is this deprecated?
@Deprecated @Deprecated
public SkinServer getGatewayServer() { public SkinServer getGatewayServer() {
return this.skinServers.get(0); return skinServers.get(0);
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
@ -266,7 +261,9 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
} }
public static CompletableFuture<PreviewTextureManager> getPreviewTextureManager(GameProfile profile) { public static CompletableFuture<PreviewTextureManager> getPreviewTextureManager(GameProfile profile) {
return INSTANCE.getGatewayServer().getPreviewTextures(profile).thenApply(PreviewTextureManager::new); return INSTANCE.getGatewayServer().getPreviewTextures(profile).thenApply(mcu -> {
return new PreviewTextureManager(mcu.getTextures());
});
} }
public void addClearListener(ISkinCacheClearListener listener) { public void addClearListener(ISkinCacheClearListener listener) {
@ -279,6 +276,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
try { try {
FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "skins")); FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "skins"));
FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "hd")); FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "hd"));
TextureManager textures = Minecraft.getMinecraft().getTextureManager(); TextureManager textures = Minecraft.getMinecraft().getTextureManager();
skinCache.values().stream() skinCache.values().stream()
.flatMap(m -> m.values().stream()) .flatMap(m -> m.values().stream())
@ -313,14 +311,18 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
return loc == null ? res : loc; return loc == null ? res : loc;
} }
public void convertSkin(BufferedImage image, Graphics dest) { public void convertSkin(BufferedImage image) {
for (ISkinModifier skin : skinModifiers) { Graphics graphics = image.getGraphics();
skin.convertSkin(image, dest); convertSkin(image, graphics);
graphics.dispose();
} }
public void convertSkin(BufferedImage image, Graphics dest) {
skinModifiers.forEach(a -> a.convertSkin(image, dest));
} }
@Override @Override
public void onResourceManagerReload(IResourceManager resourceManager) { public void onResourceManagerReload(IResourceManager resourceManager) {
this.resources.onResourceManagerReload(resourceManager); resources.onResourceManagerReload(resourceManager);
} }
} }

View file

@ -3,6 +3,7 @@ package com.voxelmodpack.hdskins;
import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.IImageBuffer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -12,6 +13,13 @@ public class ImageBufferDownloadHD implements IImageBuffer {
private Graphics graphics; private Graphics graphics;
private BufferedImage image; private BufferedImage image;
private Runnable callback;
public ImageBufferDownloadHD withCallback(Runnable callback) {
this.callback = callback;
return this;
}
@Override @Override
@Nullable @Nullable
@SuppressWarnings({"SuspiciousNameCombination", "NullableProblems"}) @SuppressWarnings({"SuspiciousNameCombination", "NullableProblems"})
@ -19,6 +27,7 @@ public class ImageBufferDownloadHD implements IImageBuffer {
if (downloadedImage == null) { if (downloadedImage == null) {
return null; return null;
} }
int imageWidth = downloadedImage.getWidth(); int imageWidth = downloadedImage.getWidth();
int imageHeight = downloadedImage.getHeight(); int imageHeight = downloadedImage.getHeight();
if (imageHeight == imageWidth) { if (imageHeight == imageWidth) {
@ -61,5 +70,8 @@ public class ImageBufferDownloadHD implements IImageBuffer {
@Override @Override
public void skinAvailable() { public void skinAvailable() {
if (callback != null) {
callback.run();
}
} }
} }

View file

@ -6,26 +6,31 @@ import net.minecraft.util.ResourceLocation;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
public class PreviewTexture extends ThreadDownloadImageData { public class PreviewTexture extends ThreadDownloadImageData {
private boolean uploaded; private boolean uploaded;
private String model; private String model;
public PreviewTexture(@Nullable String model, String url, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) { public PreviewTexture(MinecraftProfileTexture texture, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) {
super(null, url, fallbackTexture, imageBuffer); super(null, texture.getUrl(), fallbackTexture, imageBuffer);
this.model = model == null ? "default" : model; model = texture.getMetadata("model");
if (model == null) {
model = "default";
}
} }
public boolean isTextureUploaded() { public boolean isTextureUploaded() {
return uploaded && this.getGlTextureId() > -1; return uploaded && getGlTextureId() > -1;
} }
@Override @Override
public void deleteGlTexture() { public void deleteGlTexture() {
super.deleteGlTexture(); super.deleteGlTexture();
this.uploaded = true; uploaded = true;
} }
public boolean hasModel() { public boolean hasModel() {
@ -35,4 +40,8 @@ public class PreviewTexture extends ThreadDownloadImageData {
public boolean usesThinArms() { public boolean usesThinArms() {
return "thin".equals(model); return "thin".equals(model);
} }
public String getModel() {
return model;
}
} }

View file

@ -2,12 +2,13 @@ package com.voxelmodpack.hdskins;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload; import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.IImageBuffer;
import net.minecraft.client.resources.SkinManager; import net.minecraft.client.resources.SkinManager.SkinAvailableCallback;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import java.awt.image.BufferedImage;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -18,37 +19,29 @@ import javax.annotation.Nullable;
*/ */
public class PreviewTextureManager { public class PreviewTextureManager {
private final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures; private final Map<Type, MinecraftProfileTexture> textures;
PreviewTextureManager(MinecraftTexturesPayload payload) { PreviewTextureManager(Map<Type, MinecraftProfileTexture> textures) {
this.textures = payload.getTextures(); this.textures = textures;
} }
@Nullable @Nullable
public PreviewTexture getPreviewTexture(ResourceLocation location, MinecraftProfileTexture.Type type, ResourceLocation def, public PreviewTexture getPreviewTexture(ResourceLocation location, Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
@Nullable SkinManager.SkinAvailableCallback callback) {
if (!textures.containsKey(type)) { if (!textures.containsKey(type)) {
return null; return null;
} }
MinecraftProfileTexture texture = textures.get(type);
IImageBuffer buffer = new ImageBufferDownloadHD();
PreviewTexture skinTexture = new PreviewTexture(texture.getMetadata("model"), texture.getUrl(), def,
type == MinecraftProfileTexture.Type.SKIN ? new IImageBuffer() {
@Override
@Nullable
public BufferedImage parseUserSkin(BufferedImage image) {
return buffer.parseUserSkin(image);
}
@Override MinecraftProfileTexture texture = textures.get(type);
public void skinAvailable() {
IImageBuffer buffer = type != Type.SKIN ? null : new ImageBufferDownloadHD().withCallback(() -> {
if (callback != null) { if (callback != null) {
callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap())); callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap()));
} }
} });
} : null);
TextureLoader.loadTexture(location, skinTexture); PreviewTexture skinTexture = new PreviewTexture(texture, def, buffer);
Minecraft.getMinecraft().getTextureManager().loadTexture(location, skinTexture);
return skinTexture; return skinTexture;
} }

View file

@ -2,6 +2,9 @@ package com.voxelmodpack.hdskins;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.voxelmodpack.hdskins.skins.MoreHttpResponses;
import com.voxelmodpack.hdskins.skins.NetClient;
import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.IImageBuffer;
import net.minecraft.client.renderer.texture.SimpleTexture; import net.minecraft.client.renderer.texture.SimpleTexture;
import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.client.renderer.texture.TextureUtil;
@ -10,12 +13,8 @@ import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpHeaders; import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -36,46 +35,48 @@ public class ThreadDownloadImageETag extends SimpleTexture {
private final File cacheFile; private final File cacheFile;
private final File eTagFile; private final File eTagFile;
private final String imageUrl; private final String imageUrl;
@Nullable @Nullable
private final IImageBuffer imageBuffer; private final IImageBuffer imageBuffer;
@Nullable @Nullable
private BufferedImage bufferedImage; private BufferedImage bufferedImage;
@Nullable @Nullable
private Thread imageThread; private Thread imageThread;
private boolean textureUploaded; private boolean textureUploaded;
public ThreadDownloadImageETag(@Nonnull File cacheFileIn, String imageUrlIn, ResourceLocation defLocation, @Nullable IImageBuffer imageBufferIn) { public ThreadDownloadImageETag(@Nonnull File cacheLocation, String url, ResourceLocation def, @Nullable IImageBuffer buffer) {
super(defLocation); super(def);
this.cacheFile = cacheFileIn; cacheFile = cacheLocation;
this.eTagFile = new File(cacheFile.getParentFile(), cacheFile.getName() + ".etag"); eTagFile = new File(cacheFile.getParentFile(), cacheFile.getName() + ".etag");
this.imageUrl = imageUrlIn; imageUrl = url;
this.imageBuffer = imageBufferIn; imageBuffer = buffer;
} }
private void checkTextureUploaded() { private void checkTextureUploaded() {
if (!this.textureUploaded) { if (!textureUploaded) {
if (this.bufferedImage != null) { if (bufferedImage != null) {
if (this.textureLocation != null) { if (textureLocation != null) {
this.deleteGlTexture(); deleteGlTexture();
} }
TextureUtil.uploadTextureImage(super.getGlTextureId(), this.bufferedImage); TextureUtil.uploadTextureImage(super.getGlTextureId(), bufferedImage);
this.textureUploaded = true; textureUploaded = true;
} }
} }
} }
public int getGlTextureId() { public int getGlTextureId() {
this.checkTextureUploaded(); checkTextureUploaded();
return super.getGlTextureId(); return super.getGlTextureId();
} }
private void setBufferedImage(@Nonnull BufferedImage bufferedImageIn) { private void setBufferedImage(@Nonnull BufferedImage bufferedImageIn) {
this.bufferedImage = bufferedImageIn; bufferedImage = bufferedImageIn;
if (this.imageBuffer != null) { if (imageBuffer != null) {
this.imageBuffer.skinAvailable(); imageBuffer.skinAvailable();
} }
} }
@ -85,24 +86,20 @@ public class ThreadDownloadImageETag extends SimpleTexture {
} }
public void loadTexture(IResourceManager resourceManager) throws IOException { public void loadTexture(IResourceManager resourceManager) throws IOException {
if (this.bufferedImage == null && this.textureLocation != null) { if (bufferedImage == null && textureLocation != null) {
super.loadTexture(resourceManager); super.loadTexture(resourceManager);
} }
if (this.imageThread == null) { if (imageThread == null) {
this.imageThread = new Thread(this::loadTexture, "Texture Downloader #" + THREAD_ID.incrementAndGet()); imageThread = new Thread(this::loadTexture, "Texture Downloader #" + THREAD_ID.incrementAndGet());
this.imageThread.setDaemon(true); imageThread.setDaemon(true);
this.imageThread.start(); imageThread.start();
} }
} }
private void loadTexture() { private void loadTexture() {
HttpResponse response = null; try (MoreHttpResponses response = new NetClient("GET", imageUrl).send()) {
try { if (response.getResponseCode() == HttpStatus.SC_NOT_FOUND) {
HttpClient client = HttpClientBuilder.create().build();
response = client.execute(new HttpGet(imageUrl));
int status = response.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_NOT_FOUND) {
// delete the cache files in case we can't connect in the future // delete the cache files in case we can't connect in the future
clearCache(); clearCache();
} else if (checkETag(response)) { } else if (checkETag(response)) {
@ -132,9 +129,6 @@ public class ThreadDownloadImageETag extends SimpleTexture {
} }
} }
LOGGER.error("Couldn't load skin {} ", imageUrl, e); LOGGER.error("Couldn't load skin {} ", imageUrl, e);
} finally {
if (response != null)
EntityUtils.consumeQuietly(response.getEntity());
} }
} }
@ -149,15 +143,15 @@ public class ThreadDownloadImageETag extends SimpleTexture {
} }
private void clearCache() { private void clearCache() {
FileUtils.deleteQuietly(this.cacheFile); FileUtils.deleteQuietly(cacheFile);
FileUtils.deleteQuietly(this.eTagFile); FileUtils.deleteQuietly(eTagFile);
} }
private boolean checkETag(HttpResponse response) { private boolean checkETag(MoreHttpResponses response) {
try { try {
if (cacheFile.isFile()) { if (cacheFile.isFile()) {
String localETag = Files.readFirstLine(eTagFile, Charsets.UTF_8); String localETag = Files.readFirstLine(eTagFile, Charsets.UTF_8);
Header remoteETag = response.getFirstHeader(HttpHeaders.ETAG); Header remoteETag = response.getResponse().getFirstHeader(HttpHeaders.ETAG);
// true if no remote etag or does match // true if no remote etag or does match
return remoteETag == null || localETag.equals(remoteETag.getValue()); return remoteETag == null || localETag.equals(remoteETag.getValue());
} }
@ -168,18 +162,18 @@ public class ThreadDownloadImageETag extends SimpleTexture {
} }
} }
private void loadTextureFromServer(HttpResponse response) { private void loadTextureFromServer(MoreHttpResponses response) {
LOGGER.debug("Downloading http texture from {} to {}", imageUrl, cacheFile); LOGGER.debug("Downloading http texture from {} to {}", imageUrl, cacheFile);
try { try {
if (response.getStatusLine().getStatusCode() / 100 == 2) { if (response.ok()) {
BufferedImage bufferedimage; BufferedImage bufferedimage;
// write the image to disk // write the image to disk
FileUtils.copyInputStreamToFile(response.getEntity().getContent(), cacheFile); FileUtils.copyInputStreamToFile(response.getInputStream(), cacheFile);
bufferedimage = ImageIO.read(cacheFile); bufferedimage = ImageIO.read(cacheFile);
// maybe write the etag to disk // maybe write the etag to disk
Header eTag = response.getFirstHeader(HttpHeaders.ETAG); Header eTag = response.getResponse().getFirstHeader(HttpHeaders.ETAG);
if (eTag != null) { if (eTag != null) {
FileUtils.write(eTagFile, eTag.getValue(), Charsets.UTF_8); FileUtils.write(eTagFile, eTag.getValue(), Charsets.UTF_8);
} }

View file

@ -17,6 +17,7 @@ import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -25,6 +26,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
public class SkinResourceManager implements IResourceManagerReloadListener { public class SkinResourceManager implements IResourceManagerReloadListener {
private final Gson GSON = new Gson();
private ExecutorService executor = Executors.newSingleThreadExecutor(); private ExecutorService executor = Executors.newSingleThreadExecutor();
@ -41,15 +43,16 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
executor = Executors.newSingleThreadExecutor(); executor = Executors.newSingleThreadExecutor();
inProgress.clear(); inProgress.clear();
converted.clear(); converted.clear();
for (String domain : resourceManager.getResourceDomains()) { for (String domain : resourceManager.getResourceDomains()) {
try { try {
for (IResource res : resourceManager.getAllResources(new ResourceLocation(domain, "textures/skins/skins.json"))) { for (IResource res : resourceManager.getAllResources(new ResourceLocation(domain, "textures/skins/skins.json"))) {
try { try {
SkinData data = getSkinData(res.getInputStream()); for (Skin s : getSkinData(res.getInputStream())) {
for (Skin s : data.skins) {
if (s.uuid != null) { if (s.uuid != null) {
uuidSkins.put(s.uuid, s); uuidSkins.put(s.uuid, s);
} }
if (s.name != null) { if (s.name != null) {
namedSkins.put(s.name, s); namedSkins.put(s.name, s);
} }
@ -58,16 +61,13 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
LiteLoaderLogger.warning(je, "Invalid skins.json in %s", res.getResourcePackName()); LiteLoaderLogger.warning(je, "Invalid skins.json in %s", res.getResourcePackName());
} }
} }
} catch (IOException e) { } catch (IOException ignored) { }
// ignore
} }
} }
} private List<Skin> getSkinData(InputStream stream) {
private SkinData getSkinData(InputStream stream) {
try { try {
return new Gson().fromJson(new InputStreamReader(stream), SkinData.class); return GSON.fromJson(new InputStreamReader(stream), SkinData.class).skins;
} finally { } finally {
IOUtils.closeQuietly(stream); IOUtils.closeQuietly(stream);
} }
@ -75,16 +75,14 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
@Nullable @Nullable
public ResourceLocation getPlayerTexture(GameProfile profile, Type type) { public ResourceLocation getPlayerTexture(GameProfile profile, Type type) {
if (type != Type.SKIN) if (type == Type.SKIN) {
// not supported
return null;
Skin skin = getSkin(profile); Skin skin = getSkin(profile);
if (skin != null) { if (skin != null) {
final ResourceLocation res = skin.getTexture(); return getConvertedResource(skin.getTexture());
return getConvertedResource(res);
} }
return null; }
return null; // not supported
} }
/** /**
@ -99,31 +97,35 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
return converted.get(res); return converted.get(res);
} }
private void loadSkinResource(@Nullable final ResourceLocation res) { /**
* read and convert in a new thread
*/
private void loadSkinResource(@Nullable ResourceLocation res) {
if (res != null) { if (res != null) {
// read and convert in a new thread if (!inProgress.containsKey(res)) {
this.inProgress.computeIfAbsent(res, r -> CompletableFuture.supplyAsync(new ImageLoader(r), executor) inProgress.put(res, scheduleConvertion(res));
.whenComplete((loc, t) -> { }
if (loc != null)
converted.put(res, loc);
else {
LogManager.getLogger().warn("Errored while processing {}. Using original.", res, t);
converted.put(res, res);
} }
}));
} }
private Future<ResourceLocation> scheduleConvertion(ResourceLocation res) {
return CompletableFuture.supplyAsync(new ImageLoader(res), executor).whenComplete((result, error) -> {
if (result == null) {
result = res;
LogManager.getLogger().warn("Errored while processing {}. Using original.", res, error);
}
converted.put(res, result);
});
} }
@Nullable @Nullable
private Skin getSkin(GameProfile profile) { private Skin getSkin(GameProfile profile) {
Skin skin = this.uuidSkins.get(profile.getId()); Skin skin = uuidSkins.get(profile.getId());
if (skin == null) { if (skin != null) {
skin = this.namedSkins.get(profile.getName());
}
return skin; return skin;
} }
return namedSkins.get(profile.getName());
}
} }