minecraft-src/net/minecraft/client/renderer/texture/SkinTextureDownloader.java
2025-07-04 03:45:38 +03:00

176 lines
5.6 KiB
Java

package net.minecraft.client.renderer.texture;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.FileUtil;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class SkinTextureDownloader {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int SKIN_WIDTH = 64;
private static final int SKIN_HEIGHT = 64;
private static final int LEGACY_SKIN_HEIGHT = 32;
public static CompletableFuture<ResourceLocation> downloadAndRegisterSkin(ResourceLocation textureLocation, Path path, String url, boolean isLegacySkin) {
return CompletableFuture.supplyAsync(() -> {
NativeImage nativeImage;
try {
nativeImage = downloadSkin(path, url);
} catch (IOException var5) {
throw new UncheckedIOException(var5);
}
return isLegacySkin ? processLegacySkin(nativeImage, url) : nativeImage;
}, Util.nonCriticalIoPool().forName("downloadTexture")).thenCompose(nativeImage -> registerTextureInManager(textureLocation, nativeImage));
}
private static NativeImage downloadSkin(Path path, String url) throws IOException {
if (Files.isRegularFile(path, new LinkOption[0])) {
LOGGER.debug("Loading HTTP texture from local cache ({})", path);
InputStream inputStream = Files.newInputStream(path);
NativeImage var17;
try {
var17 = NativeImage.read(inputStream);
} catch (Throwable var14) {
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable var12) {
var14.addSuppressed(var12);
}
}
throw var14;
}
if (inputStream != null) {
inputStream.close();
}
return var17;
} else {
HttpURLConnection httpURLConnection = null;
LOGGER.debug("Downloading HTTP texture from {} to {}", url, path);
URI uRI = URI.create(url);
NativeImage iOException;
try {
httpURLConnection = (HttpURLConnection)uRI.toURL().openConnection(Minecraft.getInstance().getProxy());
httpURLConnection.setDoInput(true);
httpURLConnection.setDoOutput(false);
httpURLConnection.connect();
int i = httpURLConnection.getResponseCode();
if (i / 100 != 2) {
throw new IOException("Failed to open " + uRI + ", HTTP error code: " + i);
}
byte[] bs = httpURLConnection.getInputStream().readAllBytes();
try {
FileUtil.createDirectoriesSafe(path.getParent());
Files.write(path, bs, new OpenOption[0]);
} catch (IOException var13) {
LOGGER.warn("Failed to cache texture {} in {}", url, path);
}
iOException = NativeImage.read(bs);
} finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
return iOException;
}
}
private static CompletableFuture<ResourceLocation> registerTextureInManager(ResourceLocation location, NativeImage image) {
Minecraft minecraft = Minecraft.getInstance();
return CompletableFuture.supplyAsync(() -> {
minecraft.getTextureManager().register(location, new DynamicTexture(location::toString, image));
return location;
}, minecraft);
}
private static NativeImage processLegacySkin(NativeImage image, String url) {
int i = image.getHeight();
int j = image.getWidth();
if (j == 64 && (i == 32 || i == 64)) {
boolean bl = i == 32;
if (bl) {
NativeImage nativeImage = new NativeImage(64, 64, true);
nativeImage.copyFrom(image);
image.close();
image = nativeImage;
nativeImage.fillRect(0, 32, 64, 32, 0);
nativeImage.copyRect(4, 16, 16, 32, 4, 4, true, false);
nativeImage.copyRect(8, 16, 16, 32, 4, 4, true, false);
nativeImage.copyRect(0, 20, 24, 32, 4, 12, true, false);
nativeImage.copyRect(4, 20, 16, 32, 4, 12, true, false);
nativeImage.copyRect(8, 20, 8, 32, 4, 12, true, false);
nativeImage.copyRect(12, 20, 16, 32, 4, 12, true, false);
nativeImage.copyRect(44, 16, -8, 32, 4, 4, true, false);
nativeImage.copyRect(48, 16, -8, 32, 4, 4, true, false);
nativeImage.copyRect(40, 20, 0, 32, 4, 12, true, false);
nativeImage.copyRect(44, 20, -8, 32, 4, 12, true, false);
nativeImage.copyRect(48, 20, -16, 32, 4, 12, true, false);
nativeImage.copyRect(52, 20, -8, 32, 4, 12, true, false);
}
setNoAlpha(image, 0, 0, 32, 16);
if (bl) {
doNotchTransparencyHack(image, 32, 0, 64, 32);
}
setNoAlpha(image, 0, 16, 64, 32);
setNoAlpha(image, 16, 48, 48, 64);
return image;
} else {
image.close();
throw new IllegalStateException("Discarding incorrectly sized (" + j + "x" + i + ") skin texture from " + url);
}
}
private static void doNotchTransparencyHack(NativeImage image, int minX, int minY, int maxX, int maxY) {
for (int i = minX; i < maxX; i++) {
for (int j = minY; j < maxY; j++) {
int k = image.getPixel(i, j);
if (ARGB.alpha(k) < 128) {
return;
}
}
}
for (int i = minX; i < maxX; i++) {
for (int jx = minY; jx < maxY; jx++) {
image.setPixel(i, jx, image.getPixel(i, jx) & 16777215);
}
}
}
private static void setNoAlpha(NativeImage image, int minX, int minY, int maxX, int maxY) {
for (int i = minX; i < maxX; i++) {
for (int j = minY; j < maxY; j++) {
image.setPixel(i, j, ARGB.opaque(image.getPixel(i, j)));
}
}
}
}