Merge branch 'master' into hdskins_rewrites

This commit is contained in:
Sollace 2018-08-06 20:27:12 +02:00
parent fa44676aa7
commit 6995776e72
14 changed files with 253 additions and 193 deletions

View file

@ -9,8 +9,7 @@ import com.google.common.collect.HashBiMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
@ -58,10 +57,10 @@ import javax.annotation.Nonnull;
public final class HDSkinManager implements IResourceManagerReloadListener {
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
public static final ExecutorService skinUploadExecutor = Executors.newSingleThreadExecutor();
public static final ExecutorService skinDownloadExecutor = Executors.newFixedThreadPool(8);
private static final ExecutorService skinDownloadExecutor = Executors.newFixedThreadPool(8);
public static final ListeningExecutorService skinUploadExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
public static final HDSkinManager INSTANCE = new HDSkinManager();

View file

@ -3,7 +3,6 @@ package com.voxelmodpack.hdskins;
import com.google.common.collect.Maps;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.voxelmodpack.hdskins.skins.CallableFutures;
import net.minecraft.client.renderer.IImageBuffer;
@ -23,20 +22,20 @@ public class PreviewTextureManager {
private final GameProfile profile;
private Map<Type, MinecraftProfileTexture> textures = null;
private Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = null;
PreviewTextureManager(GameProfile profile) {
this.profile = profile;
}
public CompletableFuture<PreviewTexture> getPreviewTexture(ResourceLocation location, Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
public CompletableFuture<PreviewTexture> getPreviewTexture(ResourceLocation location, MinecraftProfileTexture.Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
return CallableFutures.asyncFailableFuture(() ->
loadPreviewTexture(location, type, def, callback)
, HDSkinManager.skinUploadExecutor);
}
@Nullable
private PreviewTexture loadPreviewTexture(ResourceLocation location, Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
private PreviewTexture loadPreviewTexture(ResourceLocation location, MinecraftProfileTexture.Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
if (textures == null) {
textures = HDSkinManager.INSTANCE.getGatewayServer().getProfileTextures(profile);
}
@ -47,7 +46,7 @@ public class PreviewTextureManager {
MinecraftProfileTexture texture = textures.get(type);
IImageBuffer buffer = type != Type.SKIN ? null : new ImageBufferDownloadHD().withCallback(() -> {
IImageBuffer buffer = type != MinecraftProfileTexture.Type.SKIN ? null : new ImageBufferDownloadHD().withCallback(() -> {
if (callback != null) {
callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap()));
}

View file

@ -2,6 +2,7 @@ package com.voxelmodpack.hdskins;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.voxelmodpack.hdskins.skins.MoreHttpResponses;
import com.voxelmodpack.hdskins.skins.NetClient;
import net.minecraft.client.renderer.IImageBuffer;
@ -12,9 +13,7 @@ import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.FileUtils;
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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -97,9 +96,9 @@ public class ThreadDownloadImageETag extends SimpleTexture {
private void loadTexture() {
try (NetClient client = new NetClient("GET", imageUrl)) {
CloseableHttpResponse response = client.getResponse();
MoreHttpResponses response = client.getResponse();
if (client.getResponseCode() == HttpStatus.SC_NOT_FOUND) {
if (client.getResponse().getResponseCode() == HttpStatus.SC_NOT_FOUND) {
// delete the cache files in case we can't connect in the future
clearCache();
} else if (checkETag(response)) {
@ -147,11 +146,11 @@ public class ThreadDownloadImageETag extends SimpleTexture {
FileUtils.deleteQuietly(eTagFile);
}
private boolean checkETag(HttpResponse response) {
private boolean checkETag(MoreHttpResponses response) {
try {
if (cacheFile.isFile()) {
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
return remoteETag == null || localETag.equals(remoteETag.getValue());
@ -163,18 +162,18 @@ public class ThreadDownloadImageETag extends SimpleTexture {
return false; // it failed, so re-fetch.
}
private void loadTextureFromServer(HttpResponse response) {
private void loadTextureFromServer(MoreHttpResponses response) {
LOGGER.debug("Downloading http texture from {} to {}", imageUrl, cacheFile);
try {
if (response.getStatusLine().getStatusCode() / 100 == 2) {
if (response.getResponseCode() / 100 == 2) {
BufferedImage bufferedimage;
// write the image to disk
FileUtils.copyInputStreamToFile(response.getEntity().getContent(), cacheFile);
FileUtils.copyInputStreamToFile(response.getInputStream(), cacheFile);
bufferedimage = ImageIO.read(cacheFile);
// maybe write the etag to disk
Header eTag = response.getFirstHeader(HttpHeaders.ETAG);
Header eTag = response.getResponse().getFirstHeader(HttpHeaders.ETAG);
if (eTag != null) {
FileUtils.write(eTagFile, eTag.getValue(), Charsets.UTF_8);
}

View file

@ -8,6 +8,7 @@ import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.LocalTexture;
import com.voxelmodpack.hdskins.LocalTexture.IBlankSkinSupplier;
import com.voxelmodpack.hdskins.PreviewTextureManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.SkinManager;
import net.minecraft.entity.EntityLivingBase;
@ -19,6 +20,7 @@ import net.minecraft.util.ResourceLocation;
import java.io.File;
import java.util.Map;
@SuppressWarnings("EntityConstructor")
public class EntityPlayerModel extends EntityLivingBase implements IBlankSkinSupplier {

View file

@ -16,6 +16,7 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.skins.SkinUpload;
import com.voxelmodpack.hdskins.skins.SkinUploadResponse;
import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG;
@ -544,7 +545,7 @@ public class GuiSkins extends GameGui {
btnUpload.enabled = canUpload();
HDSkinManager.INSTANCE.getGatewayServer()
.uploadSkin(mc.getSession(), path, textureType, getMetadata())
.uploadSkin(mc.getSession(), new SkinUpload(textureType, path, getMetadata()))
.thenAccept(this::onUploadComplete)
.exceptionally(this::onUploadFailure);
}

View file

@ -1,13 +1,10 @@
package com.voxelmodpack.hdskins.skins;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import org.apache.logging.log4j.util.Strings;
import com.google.gson.JsonParseException;
@ -32,19 +29,23 @@ public abstract class AbstractSkinServer implements SkinServer {
@Override
public Map<Type, MinecraftProfileTexture> getProfileTextures(GameProfile profile) {
try {
MinecraftTexturesPayload payload = getProfileData(profile);
if (payload != null && payload.getTextures() != null) {
return payload.getTextures();
}
} catch (IOException ignored) {
}
return Collections.emptyMap();
}
@Override
public final CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image, Type type, Map<String, String> metadata) {
public CompletableFuture<SkinUploadResponse> uploadSkin(Session session, SkinUpload upload) {
return CallableFutures.asyncFailableFuture(() -> {
return doUpload(session, image, type, metadata);
return doUpload(session, upload);
}, HDSkinManager.skinUploadExecutor);
}
@ -55,7 +56,9 @@ public abstract class AbstractSkinServer implements SkinServer {
}
}
protected abstract SkinUploadResponse doUpload(Session session, URI image, Type type, Map<String, String> metadata) throws AuthenticationException, IOException;
protected abstract MinecraftTexturesPayload getProfileData(GameProfile profile) throws IOException;
protected abstract SkinUploadResponse doUpload(Session session, SkinUpload upload) throws AuthenticationException, IOException;
@Override
public String toString() {

View file

@ -1,16 +1,13 @@
package com.voxelmodpack.hdskins.skins;
import java.io.IOException;
import java.net.URI;
import java.util.Locale;
import java.util.Map;
import org.apache.http.HttpStatus;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import com.mojang.util.UUIDTypeAdapter;
@ -26,47 +23,45 @@ public class BethlehemSkinServer extends AbstractSkinServer {
}
@Override
public MinecraftTexturesPayload getProfileData(GameProfile profile) {
public MinecraftTexturesPayload getProfileData(GameProfile profile) throws IOException {
try (NetClient client = new NetClient("GET", getPath(profile))) {
if (client.getResponseCode() == HttpStatus.SC_OK) {
return gson.fromJson(client.getResponseText(), MinecraftTexturesPayload.class);
if (client.getResponse().ok()) {
return client.getResponse().json(MinecraftTexturesPayload.class);
}
} catch (IOException e) {
}
return null;
}
@Override
protected SkinUploadResponse doUpload(Session session, URI image, Type type, Map<String, String> metadata) throws AuthenticationException, IOException {
protected SkinUploadResponse doUpload(Session session, SkinUpload upload) throws AuthenticationException, IOException {
SkinServer.verifyServerConnection(session, SERVER_ID);
try (NetClient client = new NetClient("POST", address)) {
client.putHeaders(createHeaders(session, type, image, metadata));
client.putHeaders(createHeaders(session, upload));
if (image != null) {
client.putFile(type.toString().toLowerCase(Locale.US), "image/png", image);
if (upload.getImage() != null) {
client.putFile(upload.getType().toString().toLowerCase(Locale.US), "image/png", upload.getImage());
}
if (client.getResponseCode() == HttpStatus.SC_OK) {
return new SkinUploadResponse(client.getResponseText());
if (client.getResponse().ok()) {
return new SkinUploadResponse(client.getResponse().text());
}
throw new IOException(client.getResponseText());
throw new IOException(client.getResponse().text());
}
}
protected Map<String, ?> createHeaders(Session session, Type type, URI image, Map<String, String> metadata) {
protected Map<String, ?> createHeaders(Session session, SkinUpload upload) {
Builder<String, Object> builder = ImmutableMap.<String, Object>builder()
.put("accessToken", session.getToken())
.put("user", session.getUsername())
.put("uuid", UUIDTypeAdapter.fromUUID(session.getProfile().getId()))
.put("type", type.toString().toLowerCase(Locale.US));
.put("type", upload.getType().toString().toLowerCase(Locale.US));
if (image == null) {
if (upload.getImage() == null) {
builder.put("clear", "1");
} else {
builder.put("model", metadata.getOrDefault("mode", "default"));
builder.put("model", upload.getMetadata().getOrDefault("mode", "default"));
}
return builder.build();

View file

@ -11,16 +11,18 @@ import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import com.mojang.util.UUIDTypeAdapter;
import net.minecraft.util.Session;
import org.apache.http.HttpStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
@ServerType("legacy")
@ -49,21 +51,14 @@ public class LegacySkinServer extends AbstractSkinServer {
return map;
}
@SuppressWarnings("deprecation")
@Override
public MinecraftTexturesPayload getProfileData(GameProfile profile) {
public MinecraftTexturesPayload getProfileData(GameProfile profile) throws IOException {
ImmutableMap.Builder<Type, MinecraftProfileTexture> builder = ImmutableMap.builder();
for (Type type : Type.values()) {
String url = getPath(address, type, profile);
try (NetClient client = new NetClient("GET", url)) {
if (client.getResponseCode() != HttpStatus.SC_OK) {
throw new IOException("Bad response code: " + client.getResponseCode());
}
builder.put(type, new MinecraftProfileTexture(url, null));
logger.debug("Found skin for {} at {}", profile.getName(), url);
try {
builder.put(type, loadProfileTexture(profile, url));
} catch (IOException e) {
logger.trace("Couldn't find texture for {} at {}. Does it exist?", profile.getName(), url, e);
}
@ -72,25 +67,32 @@ public class LegacySkinServer extends AbstractSkinServer {
Map<Type, MinecraftProfileTexture> map = builder.build();
if (map.isEmpty()) {
logger.debug("No textures found for {} at {}", profile, address);
return null;
throw new IOException(String.format("No textures found for %s at %s", profile, address));
}
return TexturesPayloadBuilder.createTexturesPayload(profile, map);
}
private MinecraftProfileTexture loadProfileTexture(GameProfile profile, String url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
if (urlConnection.getResponseCode() / 100 != 2) {
throw new IOException("Bad response code: " + urlConnection.getResponseCode() + ". URL: " + url);
}
logger.debug("Found skin for {} at {}", profile.getName(), url);
return new MinecraftProfileTexture(url, null);
}
@Override
protected SkinUploadResponse doUpload(Session session, URI image, Type type, Map<String, String> metadata) throws AuthenticationException, IOException {
protected SkinUploadResponse doUpload(Session session, SkinUpload skin) throws AuthenticationException, IOException {
SkinServer.verifyServerConnection(session, SERVER_ID);
try (NetClient client = new NetClient("POST", address)) {
client.putHeaders(createHeaders(session, type, image, metadata));
client.putHeaders(createHeaders(session, skin.getType(), skin.getImage(), skin.getMetadata()));
if (image != null) {
client.putFile(type.toString().toLowerCase(Locale.US), "image/png", image);
if (skin.getImage() != null) {
client.putFile(skin.getType().toString().toLowerCase(Locale.US), "image/png", skin.getImage());
}
String response = client.getResponseText();
String response = client.getResponse().text();
if (response.startsWith("ERROR: ")) { // lol @ "ERROR: OK"
response = response.substring(7);
@ -104,7 +106,6 @@ public class LegacySkinServer extends AbstractSkinServer {
}
}
protected Map<String, ?> createHeaders(Session session, Type type, URI image, Map<String, String> metadata) {
Builder<String, Object> builder = ImmutableMap.<String, Object>builder()
.put("user", session.getUsername())

View file

@ -0,0 +1,75 @@
package com.voxelmodpack.hdskins.skins;
import com.google.common.io.CharStreams;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;
/**
* Utility class for getting different response types from a http response.
*/
@FunctionalInterface
public interface MoreHttpResponses extends AutoCloseable {
CloseableHttpResponse getResponse();
default boolean ok() {
return getResponseCode() == HttpStatus.SC_OK;
}
default int getResponseCode() {
return getResponse().getStatusLine().getStatusCode();
}
default InputStream getInputStream() throws IOException {
return getResponse().getEntity().getContent();
}
default BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
}
default String text() throws IOException {
try (BufferedReader reader = getReader()) {
return CharStreams.toString(reader);
}
}
default Stream<String> lines() throws IOException {
try (BufferedReader reader = getReader()) {
return reader.lines();
}
}
default <T> T json(Class<T> type) throws IOException {
try (Reader reader = new InputStreamReader(getResponse().getEntity().getContent())) {
return SkinServer.gson.fromJson(reader, type);
}
}
default <T> T json(Type type) throws IOException {
try (Reader reader = new InputStreamReader(getResponse().getEntity().getContent())) {
return SkinServer.gson.fromJson(reader, type);
}
}
@Override
default void close() throws IOException {
this.getResponse().close();
}
static MoreHttpResponses execute(CloseableHttpClient client, HttpUriRequest request) throws IOException {
CloseableHttpResponse response = client.execute(request);
return () -> response;
}
}

View file

@ -1,23 +1,19 @@
package com.voxelmodpack.hdskins.skins;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* Ew. Why so many builders? >.<
@ -26,11 +22,19 @@ public class NetClient implements Closeable {
private static CloseableHttpClient client = null;
public static CloseableHttpClient nativeClient() {
if (client == null) {
client = HttpClients.createSystem();
}
return client;
}
private RequestBuilder rqBuilder;
private Map<String, ?> headers;
private CloseableHttpResponse response = null;
private MoreHttpResponses response;
public NetClient(String method, String uri) {
start(method, uri);
@ -49,7 +53,9 @@ public class NetClient implements Closeable {
headers = null;
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
try {
response.close();
} catch (IOException ignored) {}
response = null;
}
@ -97,17 +103,13 @@ public class NetClient implements Closeable {
}
}
if (client == null) {
client = HttpClients.createSystem();
}
response = client.execute(request);
response = MoreHttpResponses.execute(nativeClient(), request);
}
/**
* Gets or obtains the http response body.
*/
public CloseableHttpResponse getResponse() throws IOException {
public MoreHttpResponses getResponse() throws IOException {
if (response == null) {
send();
}
@ -115,33 +117,6 @@ public class NetClient implements Closeable {
return response;
}
/**
* Gets or obtains a response status code.
*/
public int getResponseCode() throws IOException {
return getResponse().getStatusLine().getStatusCode();
}
/**
* Consumes and returns the entire response body.
*/
public String getResponseText() throws IOException {
if (getResponse().getEntity() == null) {
return "";
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(getResponse().getEntity().getContent()))) {
StringBuilder builder = new StringBuilder();
int ch;
while ((ch = reader.read()) != -1) {
builder.append((char)ch);
}
return builder.toString();
}
}
@Override
public void close() throws IOException {
try {

View file

@ -8,21 +8,16 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import com.mojang.util.UUIDTypeAdapter;
import com.mumfrey.liteloader.modconfig.Exposable;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Session;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
public interface SkinServer extends Exposable {
Gson gson = new GsonBuilder()
@ -33,15 +28,13 @@ public interface SkinServer extends Exposable {
"http://skins.voxelmodpack.com",
"http://skinmanager.voxelmodpack.com"));
MinecraftTexturesPayload getProfileData(GameProfile profile);
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getProfileTextures(GameProfile profile);
CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image, MinecraftProfileTexture.Type type, Map<String, String> metadata);
CompletableFuture<SkinUploadResponse> uploadSkin(Session session, SkinUpload upload);
void validate() throws JsonParseException;
public static void verifyServerConnection(Session session, String serverId) throws AuthenticationException {
static void verifyServerConnection(Session session, String serverId) throws AuthenticationException {
MinecraftSessionService service = Minecraft.getMinecraft().getSessionService();
service.joinServer(session.getProfile(), session.getToken(), serverId);
}

View file

@ -0,0 +1,34 @@
package com.voxelmodpack.hdskins.skins;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import java.net.URI;
import java.util.Map;
import javax.annotation.Nullable;
public class SkinUpload {
private final URI image;
private final Map<String, String> metadata;
private final MinecraftProfileTexture.Type type;
public SkinUpload(MinecraftProfileTexture.Type type, @Nullable URI image, Map<String, String> metadata) {
this.image = image;
this.metadata = metadata;
this.type = type;
}
@Nullable
public URI getImage() {
return image;
}
public Map<String, String> getMetadata() {
return metadata;
}
public MinecraftProfileTexture.Type getType() {
return type;
}
}

View file

@ -5,7 +5,6 @@ import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
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;
@ -14,24 +13,16 @@ import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Session;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.util.Locale;
import java.util.Map;
@ -46,79 +37,81 @@ public class ValhallaSkinServer extends AbstractSkinServer {
super(address);
}
@Override
public MinecraftTexturesPayload getProfileData(GameProfile profile) {
try (CloseableHttpClient client = HttpClients.createSystem()) {
try (CloseableHttpResponse response = client.execute(new HttpGet(getTexturesURI(profile)))) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
public MinecraftTexturesPayload getProfileData(GameProfile profile) throws IOException {
try (MoreHttpResponses response = MoreHttpResponses.execute(NetClient.nativeClient(), new HttpGet(getTexturesURI(profile)))) {
if (response.ok()) {
return readJson(response, MinecraftTexturesPayload.class);
}
throw new IOException("Server sent non-ok response code: " + response.getResponseCode());
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected SkinUploadResponse doUpload(Session session, URI image, Type type, Map<String, String> metadata) throws AuthenticationException, IOException {
try (CloseableHttpClient client = HttpClients.createSystem()) {
authorize(client, session);
protected SkinUploadResponse doUpload(Session session, SkinUpload skin) throws AuthenticationException, IOException {
URI image = skin.getImage();
Map<String, String> metadata = skin.getMetadata();
MinecraftProfileTexture.Type type = skin.getType();
authorize(session);
try {
return upload(client, session, image, type, metadata);
return upload(session, image, type, metadata);
} catch (IOException e) {
if (e.getMessage().equals("Authorization failed")) {
accessToken = null;
authorize(client, session);
return upload(client, session, image, type, metadata);
authorize(session);
return upload(session, image, type, metadata);
}
throw e;
}
}
}
private SkinUploadResponse upload(CloseableHttpClient client, Session session, @Nullable URI image, MinecraftProfileTexture.Type type, Map<String, String> metadata) throws IOException {
private SkinUploadResponse upload(Session session, @Nullable URI image,
MinecraftProfileTexture.Type type, Map<String, String> metadata)
throws IOException {
GameProfile profile = session.getProfile();
if (image == null) {
return resetSkin(client, profile, type);
return resetSkin(profile, type);
}
switch (image.getScheme()) {
case "file":
return uploadFile(client, new File(image), profile, type, metadata);
return uploadFile(new File(image), profile, type, metadata);
case "http":
case "https":
return uploadUrl(client, image, profile, type, metadata);
return uploadUrl(image, profile, type, metadata);
default:
throw new IOException("Unsupported URI scheme: " + image.getScheme());
}
}
private SkinUploadResponse resetSkin(CloseableHttpClient client, GameProfile profile, MinecraftProfileTexture.Type type) throws IOException {
return upload(client, RequestBuilder.delete()
private SkinUploadResponse resetSkin(GameProfile profile, MinecraftProfileTexture.Type type) throws IOException {
return upload(RequestBuilder.delete()
.setUri(buildUserTextureUri(profile, type))
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
.build());
}
private SkinUploadResponse uploadFile(CloseableHttpClient client, File file, GameProfile profile, MinecraftProfileTexture.Type type, Map<String, String> metadata) throws IOException {
private SkinUploadResponse uploadFile(File file, GameProfile profile, MinecraftProfileTexture.Type type, Map<String, String> metadata) throws IOException {
MultipartEntityBuilder b = MultipartEntityBuilder.create();
b.addBinaryBody("file", file, ContentType.create("image/png"), file.getName());
metadata.forEach(b::addTextBody);
return upload(client, RequestBuilder.put()
return upload(RequestBuilder.put()
.setUri(buildUserTextureUri(profile, type))
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
.setEntity(b.build())
.build());
}
private SkinUploadResponse uploadUrl(CloseableHttpClient client, URI uri, GameProfile profile, MinecraftProfileTexture.Type type, Map<String, String> metadata) throws IOException {
return upload(client, RequestBuilder.post()
private SkinUploadResponse uploadUrl(URI uri, GameProfile profile, MinecraftProfileTexture.Type type, Map<String, String> metadata) throws IOException {
return upload(RequestBuilder.post()
.setUri(buildUserTextureUri(profile, type))
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
.addParameter("file", uri.toString())
@ -128,21 +121,20 @@ public class ValhallaSkinServer extends AbstractSkinServer {
.build());
}
private SkinUploadResponse upload(CloseableHttpClient client, HttpUriRequest request) throws IOException {
try (CloseableHttpResponse response = client.execute(request)) {
private SkinUploadResponse upload(HttpUriRequest request) throws IOException {
try (MoreHttpResponses response = MoreHttpResponses.execute(NetClient.nativeClient(), request)) {
return readJson(response, SkinUploadResponse.class);
}
}
private void authorize(CloseableHttpClient client, Session session) throws IOException, AuthenticationException {
if (accessToken != null) {
private void authorize(Session session) throws IOException, AuthenticationException {
if (this.accessToken != null) {
return;
}
GameProfile profile = session.getProfile();
AuthHandshake handshake = authHandshake(client, profile.getName());
AuthHandshake handshake = authHandshake(profile.getName());
if (handshake.offline) {
return;
@ -151,7 +143,7 @@ public class ValhallaSkinServer extends AbstractSkinServer {
// join the session server
Minecraft.getMinecraft().getSessionService().joinServer(profile, session.getToken(), handshake.serverId);
AuthResponse response = authResponse(client, profile.getName(), handshake.verifyToken);
AuthResponse response = authResponse(profile.getName(), handshake.verifyToken);
if (!response.userId.equals(profile.getId())) {
throw new IOException("UUID mismatch!"); // probably won't ever throw
}
@ -159,29 +151,21 @@ public class ValhallaSkinServer extends AbstractSkinServer {
accessToken = response.accessToken;
}
private <T> T readJson(HttpResponse resp, Class<T> cl) throws IOException {
String type = resp.getEntity().getContentType().getValue();
private <T> T readJson(MoreHttpResponses resp, Class<T> cl) throws IOException {
String type = resp.getResponse().getEntity().getContentType().getValue();
if (!"application/json".equals(type)) {
try {
throw new IOException("Server returned a non-json response!");
} finally {
EntityUtils.consumeQuietly(resp.getEntity());
}
}
try (Reader r = new InputStreamReader(resp.getEntity().getContent())) {
if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
// TODO specific error handling
throw new IOException(gson.fromJson(r, JsonObject.class).get("message").getAsString());
if (resp.ok()) {
return resp.json(cl);
}
throw new IOException(resp.json(JsonObject.class).get("message").getAsString());
}
return gson.fromJson(r, cl);
}
}
private AuthHandshake authHandshake(CloseableHttpClient client, String name) throws IOException {
try (CloseableHttpResponse resp = client.execute(RequestBuilder.post()
private AuthHandshake authHandshake(String name) throws IOException {
try (MoreHttpResponses resp = MoreHttpResponses.execute(NetClient.nativeClient(), RequestBuilder.post()
.setUri(getHandshakeURI())
.addParameter("name", name)
.build())) {
@ -189,8 +173,8 @@ public class ValhallaSkinServer extends AbstractSkinServer {
}
}
private AuthResponse authResponse(CloseableHttpClient client, String name, long verifyToken) throws IOException {
try (CloseableHttpResponse resp = client.execute(RequestBuilder.post()
private AuthResponse authResponse(String name, long verifyToken) throws IOException {
try (MoreHttpResponses resp = MoreHttpResponses.execute(NetClient.nativeClient(), RequestBuilder.post()
.setUri(getResponseURI())
.addParameter("name", name)
.addParameter("verifyToken", String.valueOf(verifyToken))

View file

@ -101,7 +101,7 @@ public class LayerPonyCustomHead<T extends EntityLivingBase> implements LayerRen
}
private ModelWrapper getModel() {
return ((IRenderPony) renderer).getModelWrapper();
return ((IRenderPony<?>) renderer).getModelWrapper();
}
@Override