mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-22 12:37:59 +01:00
Fix error handling and fix skin server response handling
This commit is contained in:
parent
89e24f3047
commit
9b90e9bfb6
12 changed files with 113 additions and 414 deletions
|
@ -51,13 +51,7 @@ import java.awt.image.BufferedImage;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -123,7 +117,14 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
for (SkinServer server : skinServers) {
|
||||
try {
|
||||
if (!server.getFeatures().contains(Feature.SYNTHETIC)) {
|
||||
server.loadProfileData(profile).getTextures().forEach(textureMap::putIfAbsent);
|
||||
server.loadProfileData(profile).getTextures().forEach((k, v) -> {
|
||||
try {
|
||||
Type t = Type.valueOf(k.toUpperCase(Locale.ROOT));
|
||||
if (t != null) {
|
||||
textureMap.put(t, v);
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
});
|
||||
if (textureMap.size() == Type.values().length) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ public class SkinUploader implements Closeable {
|
|||
if (throwable instanceof AuthenticationUnavailableException) {
|
||||
offline = true;
|
||||
} else if (throwable instanceof InvalidCredentialsException) {
|
||||
setError("hdskins.error.session");
|
||||
setError("Invalid session: Please try restarting Minecraft");
|
||||
} else if (throwable instanceof AuthenticationException) {
|
||||
throttlingNeck = true;
|
||||
} else if (throwable instanceof HttpException) {
|
||||
|
|
|
@ -2,13 +2,14 @@ package com.voxelmodpack.hdskins.resources;
|
|||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.voxelmodpack.hdskins.resources.texture.ISkinAvailableCallback;
|
||||
import com.voxelmodpack.hdskins.resources.texture.ImageBufferDownloadHD;
|
||||
import com.voxelmodpack.hdskins.server.TexturePayload;
|
||||
|
||||
import net.minecraft.client.resources.SkinManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -19,19 +20,22 @@ import javax.annotation.Nullable;
|
|||
*/
|
||||
public class PreviewTextureManager {
|
||||
|
||||
private final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures;
|
||||
private final Map<String, MinecraftProfileTexture> textures;
|
||||
|
||||
public PreviewTextureManager(MinecraftTexturesPayload payload) {
|
||||
public PreviewTextureManager(TexturePayload payload) {
|
||||
this.textures = payload.getTextures();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PreviewTexture getPreviewTexture(ResourceLocation location, MinecraftProfileTexture.Type type, ResourceLocation def, @Nullable SkinManager.SkinAvailableCallback callback) {
|
||||
if (!textures.containsKey(type)) {
|
||||
|
||||
String key = type.name().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (!textures.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MinecraftProfileTexture texture = textures.get(type);
|
||||
MinecraftProfileTexture texture = textures.get(key);
|
||||
ISkinAvailableCallback buff = new ImageBufferDownloadHD(type, () -> {
|
||||
if (callback != null) {
|
||||
callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap()));
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
package com.voxelmodpack.hdskins.resources.texture;
|
||||
|
||||
import com.voxelmodpack.hdskins.util.MoreHttpResponses;
|
||||
import net.minecraft.client.renderer.IImageBuffer;
|
||||
import net.minecraft.client.renderer.texture.SimpleTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Do not use. This will be removed in a later update.
|
||||
* Now that legacy includes the etag in the hash, it is no longer required to save it to disk.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ThreadDownloadImageETag extends SimpleTexture implements IBufferedTexture {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final AtomicInteger THREAD_ID = new AtomicInteger(0);
|
||||
private static CloseableHttpClient client = HttpClients.createSystem();
|
||||
|
||||
@Nonnull
|
||||
private final Path cacheFile;
|
||||
private final Path eTagFile;
|
||||
private final String imageUrl;
|
||||
@Nullable
|
||||
private final IImageBuffer imageBuffer;
|
||||
|
||||
@Nullable
|
||||
private BufferedImage bufferedImage;
|
||||
@Nullable
|
||||
private Thread imageThread;
|
||||
private boolean textureUploaded;
|
||||
|
||||
public ThreadDownloadImageETag(@Nonnull File cacheFileIn, String imageUrlIn, ResourceLocation defLocation, @Nullable IImageBuffer imageBufferIn) {
|
||||
super(defLocation);
|
||||
this.cacheFile = cacheFileIn.toPath();
|
||||
this.eTagFile = cacheFile.resolveSibling(cacheFile.getFileName() + ".etag");
|
||||
this.imageUrl = imageUrlIn;
|
||||
this.imageBuffer = imageBufferIn;
|
||||
}
|
||||
|
||||
private void checkTextureUploaded() {
|
||||
if (!this.textureUploaded) {
|
||||
if (this.bufferedImage != null) {
|
||||
if (this.textureLocation != null) {
|
||||
this.deleteGlTexture();
|
||||
}
|
||||
|
||||
TextureUtil.uploadTextureImage(super.getGlTextureId(), this.bufferedImage);
|
||||
this.textureUploaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGlTextureId() {
|
||||
this.checkTextureUploaded();
|
||||
return super.getGlTextureId();
|
||||
}
|
||||
|
||||
private void setBufferedImage(@Nonnull BufferedImage bufferedImageIn) {
|
||||
this.bufferedImage = bufferedImageIn;
|
||||
|
||||
if (this.imageBuffer != null) {
|
||||
this.imageBuffer.skinAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BufferedImage getBufferedImage() {
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTexture(IResourceManager resourceManager) throws IOException {
|
||||
if (this.bufferedImage == null && this.textureLocation != null) {
|
||||
super.loadTexture(resourceManager);
|
||||
}
|
||||
|
||||
if (this.imageThread == null) {
|
||||
this.imageThread = new Thread(this::loadTexture, "Texture Downloader #" + THREAD_ID.incrementAndGet());
|
||||
this.imageThread.setDaemon(true);
|
||||
this.imageThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadTexture() {
|
||||
switch (checkLocalCache()) {
|
||||
case GONE:
|
||||
clearCache();
|
||||
break;
|
||||
case OK:
|
||||
case NOPE:
|
||||
LOGGER.debug("Loading http texture from local cache ({})", cacheFile);
|
||||
try {
|
||||
// e-tag check passed. Load the local file
|
||||
setLocalCache();
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
// Nope. Local cache is corrupt. Re-download it.
|
||||
// fallthrough to load from network
|
||||
LOGGER.error("Couldn't load skin {}", cacheFile, e);
|
||||
}
|
||||
case OUTDATED:
|
||||
loadTextureFromServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setLocalCache() throws IOException {
|
||||
if (Files.isRegularFile(cacheFile)) {
|
||||
try (InputStream in = Files.newInputStream(cacheFile)) {
|
||||
BufferedImage image = ImageIO.read(in);
|
||||
if (imageBuffer != null) {
|
||||
image = imageBuffer.parseUserSkin(image);
|
||||
}
|
||||
setBufferedImage(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearCache() {
|
||||
try {
|
||||
Files.deleteIfExists(this.cacheFile);
|
||||
Files.deleteIfExists(this.eTagFile);
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private enum State {
|
||||
OUTDATED,
|
||||
GONE,
|
||||
NOPE,
|
||||
OK
|
||||
}
|
||||
|
||||
private State checkLocalCache() {
|
||||
try (CloseableHttpResponse response = client.execute(new HttpHead(imageUrl))) {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
if (code == HttpStatus.SC_NOT_FOUND) {
|
||||
return State.GONE;
|
||||
}
|
||||
if (code != HttpStatus.SC_OK) {
|
||||
return State.NOPE;
|
||||
}
|
||||
return checkETag(response) ? State.OK : State.OUTDATED;
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Couldn't load skin {} ", imageUrl, e);
|
||||
return State.NOPE;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkETag(HttpResponse response) {
|
||||
try {
|
||||
if (Files.isRegularFile(cacheFile)) {
|
||||
String localETag = Files.lines(eTagFile).limit(1).findFirst().orElse("");
|
||||
Header remoteETag = response.getFirstHeader(HttpHeaders.ETAG);
|
||||
// true if no remote etag or does match
|
||||
return remoteETag == null || localETag.equals(remoteETag.getValue());
|
||||
}
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
// it failed, so re-fetch.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadTextureFromServer() {
|
||||
LOGGER.debug("Downloading http texture from {} to {}", imageUrl, cacheFile);
|
||||
try (MoreHttpResponses resp = MoreHttpResponses.execute(client, new HttpGet(imageUrl))) {
|
||||
if (resp.ok()) {
|
||||
// write the image to disk
|
||||
Files.createDirectories(cacheFile.getParent());
|
||||
Files.copy(resp.inputStream(), cacheFile);
|
||||
|
||||
try (InputStream in = Files.newInputStream(cacheFile)) {
|
||||
BufferedImage bufferedimage = ImageIO.read(in);
|
||||
|
||||
// maybe write the etag to disk
|
||||
Header eTag = resp.response().getFirstHeader(HttpHeaders.ETAG);
|
||||
if (eTag != null) {
|
||||
Files.write(eTagFile, Collections.singleton(eTag.getValue()));
|
||||
}
|
||||
|
||||
if (imageBuffer != null) {
|
||||
bufferedimage = imageBuffer.parseUserSkin(bufferedimage);
|
||||
}
|
||||
setBufferedImage(bufferedimage);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error("Couldn\'t download http texture", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import com.google.common.collect.ImmutableMap.Builder;
|
|||
import com.google.gson.annotations.Expose;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.util.IndentedToStringStyle;
|
||||
|
@ -43,13 +42,13 @@ public class BethlehemSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException {
|
||||
public TexturePayload loadProfileData(GameProfile profile) throws IOException {
|
||||
try (MoreHttpResponses response = new NetClient("GET", getPath(profile)).send()) {
|
||||
if (!response.ok()) {
|
||||
throw new HttpException(response.response());
|
||||
}
|
||||
|
||||
return response.unwrapAsJson(MinecraftTexturesPayload.class);
|
||||
return response.requireOk().json(TexturePayload.class, "Invalid texture payload");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,9 +65,7 @@ public class BethlehemSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
try (MoreHttpResponses response = client.send()) {
|
||||
if (!response.ok()) {
|
||||
throw response.exception();
|
||||
}
|
||||
response.requireOk();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,11 @@ import com.google.gson.annotations.Expose;
|
|||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.util.IndentedToStringStyle;
|
||||
import com.voxelmodpack.hdskins.util.MoreHttpResponses;
|
||||
import com.voxelmodpack.hdskins.util.NetClient;
|
||||
import com.voxelmodpack.hdskins.util.TexturesPayloadBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
|
@ -53,39 +51,39 @@ public class LegacySkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
public TexturePayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
SkinServer.verifyServerConnection(Minecraft.getMinecraft().getSession(), SERVER_ID);
|
||||
|
||||
if (Strings.isNullOrEmpty(gateway)) {
|
||||
throw gatewayUnsupported();
|
||||
}
|
||||
|
||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> map = new EnumMap<>(MinecraftProfileTexture.Type.class);
|
||||
Map<String, MinecraftProfileTexture> map = new HashMap<>();
|
||||
for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.Type.values()) {
|
||||
map.put(type, new MinecraftProfileTexture(getPath(gateway, type, profile), null));
|
||||
map.put(type.name(), new MinecraftProfileTexture(getPath(gateway, type, profile), null));
|
||||
}
|
||||
|
||||
return TexturesPayloadBuilder.createTexturesPayload(profile, map);
|
||||
return new TexturePayload(profile, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException {
|
||||
ImmutableMap.Builder<MinecraftProfileTexture.Type, MinecraftProfileTexture> builder = ImmutableMap.builder();
|
||||
public TexturePayload loadProfileData(GameProfile profile) throws IOException {
|
||||
ImmutableMap.Builder<String, MinecraftProfileTexture> builder = ImmutableMap.builder();
|
||||
for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.Type.values()) {
|
||||
|
||||
String url = getPath(address, type, profile);
|
||||
try {
|
||||
builder.put(type, loadProfileTexture(profile, url));
|
||||
builder.put(type.name(), loadProfileTexture(profile, url));
|
||||
} catch (IOException e) {
|
||||
logger.trace("Couldn't find texture for {} at {}. Does it exist?", profile.getName(), url, e);
|
||||
}
|
||||
}
|
||||
|
||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> map = builder.build();
|
||||
Map<String, MinecraftProfileTexture> map = builder.build();
|
||||
if (map.isEmpty()) {
|
||||
throw new HttpException(String.format("No textures found for %s at %s", profile, this.address), 404, null);
|
||||
}
|
||||
return TexturesPayloadBuilder.createTexturesPayload(profile, map);
|
||||
return new TexturePayload(profile, map);
|
||||
}
|
||||
|
||||
private MinecraftProfileTexture loadProfileTexture(GameProfile profile, String url) throws IOException {
|
||||
|
@ -125,7 +123,7 @@ public class LegacySkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
MoreHttpResponses resp = client.send();
|
||||
String response = resp.text();
|
||||
String response = resp.reader().readLine();
|
||||
|
||||
if (response.startsWith("ERROR: ")) {
|
||||
response = response.substring(7);
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.voxelmodpack.hdskins.server;
|
|||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mumfrey.liteloader.modconfig.Exposable;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -29,7 +28,7 @@ public interface SkinServer extends Exposable {
|
|||
*
|
||||
* @throws IOException If any authentication or network error occurs.
|
||||
*/
|
||||
MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException;
|
||||
TexturePayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException;
|
||||
|
||||
/**
|
||||
* Synchronously uploads a skin to this server.
|
||||
|
@ -51,7 +50,7 @@ public interface SkinServer extends Exposable {
|
|||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
*/
|
||||
default MinecraftTexturesPayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
default TexturePayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
return loadProfileData(profile);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import com.google.gson.JsonObject;
|
|||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.google.gson.annotations.Expose;
|
|||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.util.IndentedToStringStyle;
|
||||
|
@ -54,14 +53,9 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
public TexturePayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
try (MoreHttpResponses response = MoreHttpResponses.execute(HTTP_CLIENT, new HttpGet(getTexturesURI(profile)))) {
|
||||
|
||||
if (response.ok()) {
|
||||
return response.unwrapAsJson(MinecraftTexturesPayload.class);
|
||||
}
|
||||
|
||||
throw new HttpException(response.response());
|
||||
return response.requireOk().json(TexturePayload.class, "Invalid texture payload");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,9 +125,7 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
|
||||
private void upload(HttpUriRequest request) throws IOException {
|
||||
try (MoreHttpResponses response = MoreHttpResponses.execute(HTTP_CLIENT, request)) {
|
||||
if (!response.ok()) {
|
||||
throw response.exception();
|
||||
}
|
||||
response.requireOk();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +155,7 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
.setUri(getHandshakeURI())
|
||||
.addParameter("name", name)
|
||||
.build())) {
|
||||
return resp.unwrapAsJson(AuthHandshake.class);
|
||||
return resp.requireOk().json(AuthHandshake.class, "Invalid handshake response");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +165,7 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
.addParameter("name", name)
|
||||
.addParameter("verifyToken", String.valueOf(verifyToken))
|
||||
.build())) {
|
||||
return resp.unwrapAsJson(AuthResponse.class);
|
||||
return resp.requireOk().json(AuthResponse.class, "Invalid auth response");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.google.gson.Gson;
|
|||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.*;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
|
@ -53,9 +52,7 @@ public class YggdrasilSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
|
||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = new HashMap<>();
|
||||
public TexturePayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
|
||||
Minecraft client = Minecraft.getMinecraft();
|
||||
MinecraftSessionService session = client.getSessionService();
|
||||
|
@ -68,13 +65,16 @@ public class YggdrasilSkinServer implements SkinServer {
|
|||
}
|
||||
profile = newProfile;
|
||||
|
||||
Map<String, MinecraftProfileTexture> textures = new HashMap<>();
|
||||
try {
|
||||
textures.putAll(session.getTextures(profile, requireSecure));
|
||||
session.getTextures(profile, requireSecure).forEach((k, v) -> {
|
||||
textures.put(k.name(), v);
|
||||
});
|
||||
} catch (InsecureTextureException e) {
|
||||
HDSkinManager.logger.error(e);
|
||||
}
|
||||
|
||||
return TexturesPayloadBuilder.createTexturesPayload(profile, textures);
|
||||
return new TexturePayload(profile, textures);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package com.voxelmodpack.hdskins.util;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.*;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import com.voxelmodpack.hdskins.server.HttpException;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpStatus;
|
||||
|
@ -17,12 +17,9 @@ import org.apache.http.message.BasicNameValuePair;
|
|||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Utility class for getting different response types from a http response.
|
||||
|
@ -33,89 +30,6 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||
.create();
|
||||
|
||||
CloseableHttpResponse response();
|
||||
|
||||
default boolean ok() {
|
||||
return responseCode() < HttpStatus.SC_MULTIPLE_CHOICES;
|
||||
}
|
||||
|
||||
default boolean json() {
|
||||
return "application/json".contentEquals(contentType().getMimeType());
|
||||
}
|
||||
|
||||
default int responseCode() {
|
||||
return response().getStatusLine().getStatusCode();
|
||||
}
|
||||
|
||||
default Optional<HttpEntity> entity() {
|
||||
return Optional.ofNullable(response().getEntity());
|
||||
}
|
||||
|
||||
default ContentType contentType() {
|
||||
return entity()
|
||||
.map(ContentType::get)
|
||||
.orElse(ContentType.DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
default InputStream inputStream() throws IOException {
|
||||
return response().getEntity().getContent();
|
||||
}
|
||||
|
||||
default BufferedReader reader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(inputStream(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
default byte[] bytes() throws IOException {
|
||||
try (InputStream input = inputStream()) {
|
||||
return ByteStreams.toByteArray(input);
|
||||
}
|
||||
}
|
||||
|
||||
default String text() throws IOException {
|
||||
try (BufferedReader reader = reader()) {
|
||||
return CharStreams.toString(reader);
|
||||
}
|
||||
}
|
||||
|
||||
default Stream<String> lines() throws IOException {
|
||||
try (BufferedReader reader = reader()) {
|
||||
return reader.lines();
|
||||
}
|
||||
}
|
||||
|
||||
default <T> T json(Class<T> type, String errorMessage) throws IOException {
|
||||
return json((Type)type, errorMessage);
|
||||
}
|
||||
|
||||
default <T> T json(Type type, String errorMessage) throws IOException {
|
||||
if (!json()) {
|
||||
String text = text();
|
||||
HDSkinManager.logger.error(errorMessage, text);
|
||||
throw new IOException(text);
|
||||
}
|
||||
|
||||
try (BufferedReader reader = reader()) {
|
||||
return GSON.fromJson(reader, type);
|
||||
}
|
||||
}
|
||||
|
||||
default <T> T unwrapAsJson(Type type) throws IOException {
|
||||
if (ok()) {
|
||||
return json(type, "Server returned a non-json response!");
|
||||
}
|
||||
|
||||
throw exception();
|
||||
}
|
||||
|
||||
default IOException exception() throws IOException {
|
||||
return new IOException(json(JsonObject.class, "Server error wasn't in json: {}").get("message").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
default void close() throws IOException {
|
||||
response().close();
|
||||
}
|
||||
|
||||
static MoreHttpResponses execute(CloseableHttpClient client, HttpUriRequest request) throws IOException {
|
||||
CloseableHttpResponse response = client.execute(request);
|
||||
return () -> response;
|
||||
|
@ -128,4 +42,71 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
)
|
||||
.toArray(NameValuePair[]::new);
|
||||
}
|
||||
|
||||
CloseableHttpResponse response();
|
||||
|
||||
default boolean contentTypeMatches(String contentType) {
|
||||
return contentType.contentEquals(entity()
|
||||
.map(ContentType::get)
|
||||
.orElse(ContentType.DEFAULT_TEXT)
|
||||
.getMimeType()
|
||||
);
|
||||
}
|
||||
|
||||
default int responseCode() {
|
||||
return response().getStatusLine().getStatusCode();
|
||||
}
|
||||
|
||||
default Optional<HttpEntity> entity() {
|
||||
return Optional.ofNullable(response().getEntity());
|
||||
}
|
||||
|
||||
default BufferedReader reader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(response().getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
default String text() throws IOException {
|
||||
try (BufferedReader reader = reader()) {
|
||||
return CharStreams.toString(reader);
|
||||
}
|
||||
}
|
||||
|
||||
default <T> T json(Class<T> type, String errorMessage) throws IOException {
|
||||
|
||||
if (!contentTypeMatches("application/json")) {
|
||||
String text = text();
|
||||
HDSkinManager.logger.error(errorMessage, text);
|
||||
throw new HttpException(text, responseCode(), null);
|
||||
}
|
||||
|
||||
String text = text();
|
||||
|
||||
T t = GSON.fromJson(text, type);
|
||||
if (t == null) {
|
||||
throw new HttpException(errorMessage + "\n " + text, responseCode(), null);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
default boolean ok() {
|
||||
return responseCode() < HttpStatus.SC_MULTIPLE_CHOICES;
|
||||
}
|
||||
|
||||
default MoreHttpResponses requireOk() throws IOException {
|
||||
if (!ok()) {
|
||||
JsonObject json = json(JsonObject.class, "Server did not respond correctly. Status Code " + responseCode());
|
||||
if (json.has("message")) {
|
||||
throw new HttpException(json.get("message").getAsString(), responseCode(), null);
|
||||
} else {
|
||||
throw new HttpException(json.toString(), responseCode(), null);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void close() throws IOException {
|
||||
response().close();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package com.voxelmodpack.hdskins.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Use this to build a {@link MinecraftTexturesPayload} object. This is
|
||||
* required because it has no useful constructor. This uses reflection
|
||||
* via Gson to create a new instance and populate the fields.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class TexturesPayloadBuilder {
|
||||
|
||||
private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
||||
|
||||
public static MinecraftTexturesPayload createTexturesPayload(GameProfile profile, Map<Type, MinecraftProfileTexture> textures) {
|
||||
// This worked fine as is before I started using sub-classes.
|
||||
MinecraftTexturesPayload payload = gson.fromJson(gson.toJson(new TexturesPayloadBuilder(profile)), MinecraftTexturesPayload.class);
|
||||
payload.getTextures().putAll(textures);
|
||||
return payload;
|
||||
}
|
||||
|
||||
private long timestamp;
|
||||
|
||||
private UUID profileId;
|
||||
private String profileName;
|
||||
|
||||
private boolean isPublic;
|
||||
|
||||
private Map<Type, MinecraftProfileTexture> textures;
|
||||
|
||||
private TexturesPayloadBuilder(GameProfile profile) {
|
||||
profileId = profile.getId();
|
||||
profileName = profile.getName();
|
||||
timestamp = System.currentTimeMillis();
|
||||
|
||||
isPublic = true;
|
||||
|
||||
this.textures = new HashMap<>();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue