From 89ecc9f916dd3d30e0d38335d623fc16d26e1d1f Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sun, 8 Jul 2018 03:34:35 -0400 Subject: [PATCH 1/6] Update SkinServer to be more extensible. --- .../voxelmodpack/hdskins/gui/GuiSkins.java | 49 +++++++++++-------- .../hdskins/skins/CallableFutures.java | 26 ++++++++++ .../hdskins/skins/LegacySkinServer.java | 27 +++++----- .../hdskins/skins/SkinServer.java | 14 ++++-- .../hdskins/skins/SkinUploadResponse.java | 3 -- 5 files changed, 81 insertions(+), 38 deletions(-) create mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/skins/CallableFutures.java diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java index 4d591a9c..a2a46639 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java @@ -4,15 +4,13 @@ import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.ELYTRA; import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.SKIN; import static net.minecraft.client.renderer.GlStateManager.*; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; +import com.google.common.collect.ImmutableMap; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mumfrey.liteloader.util.log.LiteLoaderLogger; import com.voxelmodpack.hdskins.HDSkinManager; import com.voxelmodpack.hdskins.skins.SkinUploadResponse; import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiButton; @@ -37,13 +35,17 @@ import org.lwjgl.opengl.GL11; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.net.URI; import java.nio.DoubleBuffer; -import java.nio.file.Path; +import java.util.Map; + import javax.annotation.Nullable; import javax.imageio.ImageIO; -import javax.swing.*; +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +public class GuiSkins extends GuiScreen { -public class GuiSkins extends GuiScreen implements FutureCallback { private static final int MAX_SKIN_DIMENSION = 1024; private int updateCounter = 0; @@ -86,7 +88,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback getMetadata() { + return ImmutableMap.of("model", this.thinArmType ? "slim" : "default"); } private void setUploadError(@Nullable String error) { - this.uploadError = error != null && error.startsWith("ERROR: ") ? error.substring(7) : error; + this.uploadError = error; this.btnUpload.enabled = true; } - @Override - public void onSuccess(@Nullable SkinUploadResponse result) { - if (result != null) - onUploadComplete(result); - } - @Override - public void onFailure(Throwable t) { + private Void onFailure(Throwable t) { LogManager.getLogger().warn("Upload failed", t); this.setUploadError(t.toString()); this.uploadingSkin = false; + + return null; } private void onUploadComplete(SkinUploadResponse response) { LiteLoaderLogger.info("Upload completed with: %s", response); this.uploadingSkin = false; - if (!"OK".equalsIgnoreCase(response.getMessage())) { + if (!response.isSuccess()) { this.setUploadError(response.getMessage()); } else { this.pendingRemoteSkinRefresh = true; diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/CallableFutures.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/CallableFutures.java new file mode 100644 index 00000000..53f087a2 --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/CallableFutures.java @@ -0,0 +1,26 @@ +package com.voxelmodpack.hdskins.skins; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class CallableFutures { + + public static CompletableFuture asyncFailableFuture(Callable call, Executor exec) { + CompletableFuture ret = new CompletableFuture<>(); + exec.execute(() -> { + try { + ret.complete(call.call()); + } catch (Throwable e) { + ret.completeExceptionally(e); + } + }); + return ret; + } + + public static CompletableFuture failedFuture(Exception e) { + CompletableFuture ret = new CompletableFuture<>(); + ret.completeExceptionally(e); + return ret; + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java index fe37fb74..766b4dac 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java @@ -3,8 +3,6 @@ package com.voxelmodpack.hdskins.skins; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.minecraft.MinecraftProfileTexture; @@ -20,13 +18,15 @@ import org.apache.logging.log4j.Logger; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; -import java.nio.file.Path; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.annotation.Nullable; public class LegacySkinServer implements SkinServer { @@ -88,19 +88,24 @@ public class LegacySkinServer implements SkinServer { } @Override - public ListenableFuture uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinSkinType) { + public CompletableFuture uploadSkin(Session session, @Nullable URI image, + MinecraftProfileTexture.Type type, Map metadata) { - if (Strings.isNullOrEmpty(this.gateway)) - return Futures.immediateFailedFuture(new NullPointerException("gateway url is blank")); + if (Strings.isNullOrEmpty(this.gateway)) { + return CallableFutures.failedFuture(new NullPointerException("gateway url is blank")); + } - return HDSkinManager.skinUploadExecutor.submit(() -> { + return CallableFutures.asyncFailableFuture(() -> { verifyServerConnection(session, SERVER_ID); - - Map data = image == null ? getClearData(session, type) : getUploadData(session, type, (thinSkinType ? "slim" : "default"), image); + String model = metadata.getOrDefault("model", "default"); + Map data = image == null ? getClearData(session, type) : getUploadData(session, type, model, image); ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(this.gateway, data); String response = upload.uploadMultipart(); + if (response.startsWith("ERROR: ")) + response = response.substring(7); return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response); - }); + + }, HDSkinManager.skinUploadExecutor); } private static Map getData(Session session, MinecraftProfileTexture.Type type, String model, String param, Object val) { @@ -116,7 +121,7 @@ public class LegacySkinServer implements SkinServer { return getData(session, type, "default", "clear", "1"); } - private static Map getUploadData(Session session, MinecraftProfileTexture.Type type, String model, Path skinFile) { + private static Map getUploadData(Session session, MinecraftProfileTexture.Type type, String model, URI skinFile) { return getData(session, type, model, type.toString().toLowerCase(Locale.US), skinFile); } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java index a51ec9c7..2aca2d0f 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java @@ -1,15 +1,17 @@ package com.voxelmodpack.hdskins.skins; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.ListenableFuture; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload; import net.minecraft.util.Session; -import java.nio.file.Path; +import java.net.URI; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; + import javax.annotation.Nullable; public interface SkinServer { @@ -18,9 +20,12 @@ public interface SkinServer { Optional loadProfileData(GameProfile profile); - Optional getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile); + default Optional getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) { + return loadProfileData(profile).map(data -> data.getTextures().get(type)); + } - ListenableFuture uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType); + CompletableFuture uploadSkin(Session session, @Nullable URI image, + MinecraftProfileTexture.Type type, Map metadata); static SkinServer from(String server) { int i = server.indexOf(':'); @@ -36,4 +41,5 @@ public interface SkinServer { } throw new IllegalArgumentException(); } + } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinUploadResponse.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinUploadResponse.java index af0043b5..680d915e 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinUploadResponse.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinUploadResponse.java @@ -2,8 +2,6 @@ package com.voxelmodpack.hdskins.skins; import com.google.common.base.MoreObjects; -import javax.annotation.Nullable; - public class SkinUploadResponse { private final boolean success; @@ -18,7 +16,6 @@ public class SkinUploadResponse { return success; } - @Nullable public String getMessage() { return message; } From 77b8357f037e7a288fabf9191b205bfb0af10d8c Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sun, 8 Jul 2018 03:35:11 -0400 Subject: [PATCH 2/6] Add Valhalla implementation --- build.gradle | 12 +- .../voxelmodpack/hdskins/HDSkinManager.java | 1 - .../hdskins/skins/ValhallaSkinServer.java | 202 +++++++++++++++++- .../upload/ThreadMultipartPostUpload.java | 2 + 4 files changed, 207 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index d234d2c6..5d3debcd 100644 --- a/build.gradle +++ b/build.gradle @@ -39,11 +39,18 @@ sourceSets { ext.refMap = 'hdskins.mixin.refmap.json' } main { - compileClasspath += hdskins.output + compileClasspath += hdskins.output + hdskins.compileClasspath ext.refMap = 'minelp.mixin.refmap.json' } } +dependencies { + // use the same version as httpclient + hdskinsCompile('org.apache.httpcomponents:httpmime:4.3.2'){ + transitive = false + } +} + litemod.json { mcversion = '1.12.r2' author = 'Verdana, Rene_Z, Mumfrey, Killjoy1221' @@ -64,6 +71,9 @@ litemod.json { jar { from sourceSets.hdskins.output from litemod + + // TODO relocate. LiteLoader excludes apache libs from classloading + from {configurations.hdskinsCompile.collect{it.isDirectory() ? it : zipTree(it)}} } sourceJar { // add hdskins sources diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java index 6d90b6f4..b65871b8 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java @@ -223,7 +223,6 @@ public final class HDSkinManager implements IResourceManagerReloadListener { this.enabled = enabled; } - @Nullable public static PreviewTexture getPreviewTexture(ResourceLocation skinResource, GameProfile profile, Type type, ResourceLocation def, @Nullable final SkinAvailableCallback callback) { TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); MinecraftProfileTexture url = INSTANCE.getGatewayServer().getPreviewTexture(type, profile).orElse(null); diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java index edaea83f..7116fac8 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java @@ -1,21 +1,55 @@ package com.voxelmodpack.hdskins.skins; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +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.yggdrasil.response.MinecraftTexturesPayload; +import com.mojang.util.UUIDTypeAdapter; +import com.voxelmodpack.hdskins.HDSkinManager; +import net.minecraft.client.Minecraft; import net.minecraft.util.Session; +import org.apache.http.HttpHeaders; +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 java.nio.file.Path; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.util.Locale; +import java.util.Map; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.annotation.Nullable; public class ValhallaSkinServer implements SkinServer { @SuppressWarnings("unused") private final String baseURL; + private final Gson gson = new GsonBuilder() + .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) + .create(); + + private String accessToken; public ValhallaSkinServer(String baseURL) { this.baseURL = baseURL; @@ -23,23 +57,175 @@ public class ValhallaSkinServer implements SkinServer { @Override public Optional loadProfileData(GameProfile profile) { + + try (CloseableHttpClient client = HttpClients.createSystem(); + CloseableHttpResponse response = client.execute(new HttpGet(getTexturesURI(profile)))) { + + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + + return Optional.of(readJson(response.getEntity().getContent(), MinecraftTexturesPayload.class)); + } + } catch (IOException e) { + e.printStackTrace(); + } return Optional.empty(); } @Override - public Optional getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) { - return null; + public CompletableFuture uploadSkin(Session session, @Nullable URI image, + MinecraftProfileTexture.Type type, Map metadata) { + return CallableFutures.asyncFailableFuture(() -> { + try (CloseableHttpClient client = HttpClients.createSystem()) { + authorize(client, session); + + GameProfile profile = session.getProfile(); + + if (image == null) { + return resetSkin(client, profile, type); + } + switch (image.getScheme()) { + case "file": + return uploadFile(client, new File(image), profile, type, metadata); + case "http": + case "https": + return uploadUrl(client, image, profile, type, metadata); + default: + throw new IOException("Unsupported URI scheme: " + image.getScheme()); + } + } + }, HDSkinManager.skinUploadExecutor); } - @Override - public ListenableFuture uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType) { - return null; + private SkinUploadResponse resetSkin(CloseableHttpClient client, GameProfile profile, MinecraftProfileTexture.Type type) throws IOException { + return upload(client, 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 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() + .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 metadata) throws IOException { + + return upload(client, RequestBuilder.post() + .setUri(buildUserTextureUri(profile, type)) + .addHeader(HttpHeaders.AUTHORIZATION, this.accessToken) + .addParameter("file", uri.toString()) + .addParameters(metadata.entrySet().stream() + .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) + .toArray(NameValuePair[]::new)) + .build()); + } + + private SkinUploadResponse upload(CloseableHttpClient client, HttpUriRequest request) throws IOException { + try (CloseableHttpResponse response = client.execute(request)) { + int code = response.getStatusLine().getStatusCode(); + JsonObject json = readJson(response.getEntity().getContent(), JsonObject.class); + + return new SkinUploadResponse(code == HttpStatus.SC_OK, json.get("message").getAsString()); + } + } + + + private void authorize(CloseableHttpClient client, Session session) throws IOException, AuthenticationException { + if (accessToken != null) { + return; + } + GameProfile profile = session.getProfile(); + String token = session.getToken(); + AuthHandshake handshake = authHandshake(client, profile.getName()); + + if (handshake.offline) { + return; + } + + // join the session server + Minecraft.getMinecraft().getSessionService().joinServer(profile, token, handshake.serverId); + + AuthResponse response = authResponse(client, profile.getName(), handshake.verifyToken); + if (!response.userId.equals(profile.getId())) { + throw new IOException("UUID mismatch!"); // probably won't ever throw + } + this.accessToken = response.accessToken; + + } + + private T readJson(InputStream in, Class cl) throws IOException { + try (Reader r = new InputStreamReader(in)) { + return gson.fromJson(r, cl); + } + } + + private AuthHandshake authHandshake(CloseableHttpClient client, String name) throws IOException { + try (CloseableHttpResponse resp = client.execute(RequestBuilder.post() + .setUri(getHandshakeURI()) + .addParameter("name", name) + .build())) { + return readJson(resp.getEntity().getContent(), AuthHandshake.class); + } + } + + private AuthResponse authResponse(CloseableHttpClient client, String name, long verifyToken) throws IOException { + try (CloseableHttpResponse resp = client.execute(RequestBuilder.post() + .setUri(getResponseURI()) + .addParameter("name", name) + .addParameter("verifyToken", String.valueOf(verifyToken)) + .build())) { + return readJson(resp.getEntity().getContent(), AuthResponse.class); + } + } + + private URI buildUserTextureUri(GameProfile profile, MinecraftProfileTexture.Type textureType) { + String user = UUIDTypeAdapter.fromUUID(profile.getId()); + String skinType = textureType.name().toLowerCase(Locale.US); + return URI.create(String.format("%s/user/%s/%s", this.baseURL, user, skinType)); + } + + private URI getTexturesURI(GameProfile profile) { + Preconditions.checkNotNull(profile.getId(), "profile id required for skins"); + return URI.create(String.format("%s/user/%s", this.baseURL, UUIDTypeAdapter.fromUUID(profile.getId()))); + } + + private URI getHandshakeURI() { + return URI.create(String.format("%s/auth/handshake", this.baseURL)); + } + + private URI getResponseURI() { + return URI.create(String.format("%s/auth/response", this.baseURL)); } static ValhallaSkinServer from(String server) { Matcher matcher = Pattern.compile("^valhalla:(.*)$").matcher(server); - if (matcher.find()) + if (matcher.find()) { return new ValhallaSkinServer(matcher.group(1)); + } throw new IllegalArgumentException(); } + + @SuppressWarnings("WeakerAccess") + static class AuthHandshake { + + boolean offline; + String serverId; + long verifyToken; + } + + @SuppressWarnings("WeakerAccess") + static class AuthResponse { + + String accessToken; + UUID userId; + } } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/upload/ThreadMultipartPostUpload.java b/src/hdskins/java/com/voxelmodpack/hdskins/upload/ThreadMultipartPostUpload.java index a9d22389..f4030860 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/upload/ThreadMultipartPostUpload.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/upload/ThreadMultipartPostUpload.java @@ -18,7 +18,9 @@ import javax.annotation.Nullable; * Uploader for Multipart form data * * @author Adam Mummery-Smith + * @deprecated Use httpmime multipart upload */ +@Deprecated public class ThreadMultipartPostUpload { protected final Map sourceData; From e33c8f6fda496cfb5eb89c8120f448241562f073 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sun, 8 Jul 2018 18:00:58 -0400 Subject: [PATCH 3/6] Relocate http mime --- build.gradle | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 5d3debcd..b3fde984 100644 --- a/build.gradle +++ b/build.gradle @@ -13,11 +13,13 @@ buildscript { dependencies { classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' + classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4' } } apply plugin: 'net.minecraftforge.gradle.liteloader' apply plugin: 'org.spongepowered.mixin' +apply plugin: 'com.github.johnrengelman.shadow' group = 'com.minelittlepony' version = '1.12.2.2-SNAPSHOT' @@ -39,14 +41,14 @@ sourceSets { ext.refMap = 'hdskins.mixin.refmap.json' } main { - compileClasspath += hdskins.output + hdskins.compileClasspath + compileClasspath += hdskins.output ext.refMap = 'minelp.mixin.refmap.json' } } dependencies { // use the same version as httpclient - hdskinsCompile('org.apache.httpcomponents:httpmime:4.3.2'){ + compile('org.apache.httpcomponents:httpmime:4.3.2'){ transitive = false } } @@ -71,9 +73,23 @@ litemod.json { jar { from sourceSets.hdskins.output from litemod + classifier 'base' + extension 'jar' +} +shadowJar { + extension 'litemod' + classifier "mc$minecraft.version" + baseName "mod-${project.name.toLowerCase()}" - // TODO relocate. LiteLoader excludes apache libs from classloading - from {configurations.hdskinsCompile.collect{it.isDirectory() ? it : zipTree(it)}} + from sourceSets.hdskins.output + from litemod + dependencies { + exclude dependency('deobf.com.mumfrey:liteloader:') + exclude dependency('deobf.org.ow2.asm:') + exclude 'META-INF/**' + } + relocate 'org.apache.http.entity.mime', 'com.voxelmodpack.repack.org.apache.http.entity.mime' + exclude 'dummyThing' } sourceJar { // add hdskins sources @@ -92,10 +108,15 @@ task skinZip(type: Zip) { version 'v1' } +artifacts { + archives shadowJar +} + reobf { srgJar { mappingType = 'SEARGE' } + shadowJar{} } mixin { defaultObfuscationEnv notch From ed9601680b1497885071e275079c2758f65faf8f Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sun, 8 Jul 2018 18:28:43 -0400 Subject: [PATCH 4/6] Try to fix travis --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 968917cb..48f42630 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,11 @@ language: java +install: true jdk: - oraclejdk8 +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ From a34cfed3bf7a80fe8aa71f2e9f3d84d294a0f0d8 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Mon, 9 Jul 2018 11:53:22 -0400 Subject: [PATCH 5/6] Prevent preview textures from calling the skin server multiple times for each skin time. --- .../voxelmodpack/hdskins/HDSkinManager.java | 27 +--------- .../voxelmodpack/hdskins/PreviewTexture.java | 4 +- .../hdskins/PreviewTextureManager.java | 54 +++++++++++++++++++ .../hdskins/gui/EntityPlayerModel.java | 7 ++- .../hdskins/skins/LegacySkinServer.java | 18 +++++-- .../hdskins/skins/SkinServer.java | 6 +-- .../hdskins/skins/ValhallaSkinServer.java | 3 +- 7 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/PreviewTextureManager.java diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java index b65871b8..d0f92a93 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java @@ -223,31 +223,8 @@ public final class HDSkinManager implements IResourceManagerReloadListener { this.enabled = enabled; } - public static PreviewTexture getPreviewTexture(ResourceLocation skinResource, GameProfile profile, Type type, ResourceLocation def, @Nullable final SkinAvailableCallback callback) { - TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); - MinecraftProfileTexture url = INSTANCE.getGatewayServer().getPreviewTexture(type, profile).orElse(null); - if (url == null) - return null; - - IImageBuffer buffer = new ImageBufferDownloadHD(); - PreviewTexture skinTexture = new PreviewTexture(url.getMetadata("model"), url.getUrl(), def, type == Type.SKIN ? new IImageBuffer() { - @Override - @Nullable - public BufferedImage parseUserSkin(BufferedImage image) { - return buffer.parseUserSkin(image); - } - - @Override - public void skinAvailable() { - if (callback != null) { - callback.skinAvailable(type, skinResource, new MinecraftProfileTexture(url.getUrl(), Maps.newHashMap())); - } - } - } : null); - textureManager.loadTexture(skinResource, skinTexture); - - return skinTexture; - + public static PreviewTextureManager getPreviewTextureManager(GameProfile profile) { + return new PreviewTextureManager(INSTANCE.getGatewayServer().getPreviewTextures(profile)); } public void addClearListener(ISkinCacheClearListener listener) { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTexture.java b/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTexture.java index ee6ce18e..e5430ee2 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTexture.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTexture.java @@ -12,10 +12,10 @@ public class PreviewTexture extends ThreadDownloadImageData { private String model; - public PreviewTexture(String model, String url, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) { + public PreviewTexture(@Nullable String model, String url, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) { super(null, url, fallbackTexture, imageBuffer); - this.model = model; + this.model = model == null ? "default" : model; } public boolean isTextureUploaded() { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTextureManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTextureManager.java new file mode 100644 index 00000000..057fd56f --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/PreviewTextureManager.java @@ -0,0 +1,54 @@ +package com.voxelmodpack.hdskins; + +import com.google.common.collect.Maps; +import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.IImageBuffer; +import net.minecraft.client.resources.SkinManager; +import net.minecraft.util.ResourceLocation; + +import java.awt.image.BufferedImage; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * Manager for fetching preview textures. This ensures that multiple calls + * to the skin server aren't done when fetching preview textures. + */ +public class PreviewTextureManager { + + private final Map textures; + + PreviewTextureManager(Map textures) { + this.textures = textures; + } + + @Nullable + public PreviewTexture getPreviewTexture(ResourceLocation location, MinecraftProfileTexture.Type type, ResourceLocation def, + @Nullable SkinManager.SkinAvailableCallback callback) { + if (!textures.containsKey(type)) { + 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 + public void skinAvailable() { + if (callback != null) { + callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap())); + } + } + } : null); + Minecraft.getMinecraft().getTextureManager().loadTexture(location, skinTexture); + + return skinTexture; + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/EntityPlayerModel.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/EntityPlayerModel.java index 94b1b2db..f363b58f 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/gui/EntityPlayerModel.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/EntityPlayerModel.java @@ -8,6 +8,7 @@ import com.voxelmodpack.hdskins.DynamicTextureImage; import com.voxelmodpack.hdskins.HDSkinManager; import com.voxelmodpack.hdskins.ImageBufferDownloadHD; import com.voxelmodpack.hdskins.PreviewTexture; +import com.voxelmodpack.hdskins.PreviewTextureManager; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.client.renderer.texture.TextureManager; @@ -76,8 +77,10 @@ public class EntityPlayerModel extends EntityLivingBase { this.textureManager.deleteTexture(this.remoteElytraResource); } - this.remoteSkinTexture = HDSkinManager.getPreviewTexture(this.remoteSkinResource, this.profile, Type.SKIN, getBlankSkin(), listener); - this.remoteElytraTexture = HDSkinManager.getPreviewTexture(this.remoteElytraResource, this.profile, Type.ELYTRA, getBlankElytra(), null); + PreviewTextureManager ptm = HDSkinManager.getPreviewTextureManager(this.profile); + + this.remoteSkinTexture = ptm.getPreviewTexture(this.remoteSkinResource, Type.SKIN, getBlankSkin(), listener); + this.remoteElytraTexture = ptm.getPreviewTexture(this.remoteElytraResource, Type.ELYTRA, getBlankElytra(), null); } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java index 766b4dac..c31a7c66 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java @@ -20,6 +20,8 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; +import java.util.Collections; +import java.util.EnumMap; import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -48,10 +50,15 @@ public class LegacySkinServer implements SkinServer { } @Override - public Optional getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) { - if (Strings.isNullOrEmpty(this.gateway)) - return Optional.empty(); - return Optional.of(new MinecraftProfileTexture(getPath(this.gateway, type, profile), null)); + public Map getPreviewTextures(GameProfile profile) { + if (Strings.isNullOrEmpty(this.gateway)) { + return Collections.emptyMap(); + } + Map map = new EnumMap<>(MinecraftProfileTexture.Type.class); + for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.Type.values()) { + map.put(type, new MinecraftProfileTexture(getPath(gateway, type, profile), null)); + } + return map; } @Override @@ -101,8 +108,9 @@ public class LegacySkinServer implements SkinServer { Map data = image == null ? getClearData(session, type) : getUploadData(session, type, model, image); ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(this.gateway, data); String response = upload.uploadMultipart(); - if (response.startsWith("ERROR: ")) + if (response.startsWith("ERROR: ")) { response = response.substring(7); + } return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response); }, HDSkinManager.skinUploadExecutor); diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java index 2aca2d0f..142e7840 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java @@ -7,6 +7,7 @@ import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload; import net.minecraft.util.Session; import java.net.URI; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -20,8 +21,8 @@ public interface SkinServer { Optional loadProfileData(GameProfile profile); - default Optional getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) { - return loadProfileData(profile).map(data -> data.getTextures().get(type)); + default Map getPreviewTextures(GameProfile profile) { + return loadProfileData(profile).map(MinecraftTexturesPayload::getTextures).orElse(Collections.emptyMap()); } CompletableFuture uploadSkin(Session session, @Nullable URI image, @@ -41,5 +42,4 @@ public interface SkinServer { } throw new IllegalArgumentException(); } - } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java index 7116fac8..e1e92470 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java @@ -43,7 +43,6 @@ import javax.annotation.Nullable; public class ValhallaSkinServer implements SkinServer { - @SuppressWarnings("unused") private final String baseURL; private final Gson gson = new GsonBuilder() .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) @@ -51,7 +50,7 @@ public class ValhallaSkinServer implements SkinServer { private String accessToken; - public ValhallaSkinServer(String baseURL) { + private ValhallaSkinServer(String baseURL) { this.baseURL = baseURL; } From 540ed431788d948344e865121017e687f2836ae4 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Fri, 13 Jul 2018 23:05:21 -0400 Subject: [PATCH 6/6] Make SkinServer json serializable and add a button go skins menu to show current gateway server. --- .../voxelmodpack/hdskins/HDSkinManager.java | 64 +++++++++++++++---- .../voxelmodpack/hdskins/gui/GuiSkins.java | 13 +++- .../hdskins/mod/LiteModHDSkinsMod.java | 41 +++++------- .../hdskins/mod/package-info.java | 7 -- .../hdskins/skins/IndentedToStringStyle.java | 18 ++++++ .../hdskins/skins/LegacySkinServer.java | 33 +++------- .../hdskins/skins/ServerType.java | 13 ++++ .../hdskins/skins/SkinServer.java | 18 +----- .../hdskins/skins/SkinServerSerializer.java | 34 ++++++++++ .../hdskins/skins/ValhallaSkinServer.java | 33 +++++----- .../com/minelittlepony/MineLittlePony.java | 6 +- 11 files changed, 178 insertions(+), 102 deletions(-) delete mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/mod/package-info.java create mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/skins/IndentedToStringStyle.java create mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/skins/ServerType.java create mode 100644 src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServerSerializer.java diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java index d0f92a93..a6599cbd 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java @@ -1,8 +1,11 @@ package com.voxelmodpack.hdskins; +import com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -21,7 +24,10 @@ import com.mumfrey.liteloader.util.log.LiteLoaderLogger; import com.voxelmodpack.hdskins.gui.GuiSkins; import com.voxelmodpack.hdskins.resource.SkinResourceManager; import com.voxelmodpack.hdskins.skins.AsyncCacheLoader; +import com.voxelmodpack.hdskins.skins.LegacySkinServer; +import com.voxelmodpack.hdskins.skins.ServerType; import com.voxelmodpack.hdskins.skins.SkinServer; +import com.voxelmodpack.hdskins.skins.ValhallaSkinServer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.IImageBuffer; import net.minecraft.client.renderer.texture.ITextureObject; @@ -31,13 +37,15 @@ import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManagerReloadListener; import net.minecraft.client.resources.SkinManager.SkinAvailableCallback; import net.minecraft.util.ResourceLocation; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Collections; import java.util.Date; import java.util.List; @@ -50,7 +58,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.annotation.Nullable; public final class HDSkinManager implements IResourceManagerReloadListener { @@ -68,6 +75,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener { private List clearListeners = Lists.newArrayList(); + private BiMap> skinServerTypes = HashBiMap.create(2); private List skinServers = Lists.newArrayList(); private Map> skinCache = Maps.newHashMap(); @@ -81,10 +89,17 @@ public final class HDSkinManager implements IResourceManagerReloadListener { private List skinModifiers = Lists.newArrayList(); private SkinResourceManager resources = new SkinResourceManager(); -// private ExecutorService executor = Executors.newCachedThreadPool(); + // private ExecutorService executor = Executors.newCachedThreadPool(); private Class skinsClass = null; + private HDSkinManager() { + + // register default skin server types + addSkinServerType(LegacySkinServer.class); + addSkinServerType(ValhallaSkinServer.class); + } + public void setPrefferedSkinsGuiClass(Class clazz) { skinsClass = clazz; } @@ -102,35 +117,42 @@ public final class HDSkinManager implements IResourceManagerReloadListener { } public Optional getSkinLocation(GameProfile profile1, final Type type, boolean loadIfAbsent) { - if (!enabled) + if (!enabled) { return Optional.empty(); + } ResourceLocation skin = this.resources.getPlayerTexture(profile1, type); - if (skin != null) + if (skin != null) { return Optional.of(skin); + } // try to recreate a broken gameprofile // happens when server sends a random profile with skin and displayname Property textures = Iterables.getFirst(profile1.getProperties().get("textures"), null); if (textures != null) { - MinecraftTexturesPayload texturePayload = GSON.fromJson(new String(Base64.decodeBase64(textures.getValue())), MinecraftTexturesPayload.class); + String json = new String(Base64.getDecoder().decode(textures.getValue()), StandardCharsets.UTF_8); + MinecraftTexturesPayload texturePayload = GSON.fromJson(json, MinecraftTexturesPayload.class); if (texturePayload != null) { // name is optional String name = texturePayload.getProfileName(); UUID uuid = texturePayload.getProfileId(); // uuid is required - if (uuid != null) + if (uuid != null) { profile1 = new GameProfile(uuid, name); + } // probably uses this texture for a reason. Don't mess with it. - if (!texturePayload.getTextures().isEmpty() && texturePayload.getProfileId() == null) + if (!texturePayload.getTextures().isEmpty() && texturePayload.getProfileId() == null) { return Optional.empty(); + } } } final GameProfile profile = profile1; // cannot get texture without id! - if (profile.getId() == null) return Optional.empty(); + if (profile.getId() == null) { + return Optional.empty(); + } if (!this.skinCache.containsKey(profile.getId())) { this.skinCache.put(profile.getId(), Maps.newHashMap()); @@ -148,7 +170,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener { } private String bustCache(String url) { - 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) { @@ -194,8 +216,9 @@ public final class HDSkinManager implements IResourceManagerReloadListener { for (SkinServer server : skinServers) { Optional profileData = server.loadProfileData(profile); profileData.map(MinecraftTexturesPayload::getTextures).ifPresent(it -> it.forEach(textures::putIfAbsent)); - if (textures.size() == Type.values().length) + if (textures.size() == Type.values().length) { break; + } } return textures; @@ -211,10 +234,25 @@ public final class HDSkinManager implements IResourceManagerReloadListener { return textures; } - public void addSkinServer(SkinServer skinServer) { - this.skinServers.add(0, skinServer); + public void addSkinServerType(Class type) { + Preconditions.checkArgument(!type.isInterface(), "type cannot be an interface"); + Preconditions.checkArgument(!Modifier.isAbstract(type.getModifiers()), "type cannot be abstract"); + ServerType st = type.getAnnotation(ServerType.class); + if (st == null) { + throw new IllegalArgumentException("class is not annotated with @ServerType"); + } + this.skinServerTypes.put(st.value(), type); } + public Class getSkinServerClass(String type) { + return this.skinServerTypes.get(type); + } + + public void addSkinServer(SkinServer skinServer) { + this.skinServers.add(skinServer); + } + + @Deprecated public SkinServer getGatewayServer() { return this.skinServers.get(0); } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java index a2a46639..b1ed1f93 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java @@ -4,11 +4,13 @@ import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.ELYTRA; import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.SKIN; import static net.minecraft.client.renderer.GlStateManager.*; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mumfrey.liteloader.util.log.LiteLoaderLogger; import com.voxelmodpack.hdskins.HDSkinManager; +import com.voxelmodpack.hdskins.skins.SkinServer; import com.voxelmodpack.hdskins.skins.SkinUploadResponse; import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG; import net.minecraft.client.Minecraft; @@ -57,6 +59,8 @@ public class GuiSkins extends GuiScreen { private GuiButton btnModeSkinnySkin; private GuiButton btnModeElytra; + private GuiButton btnAbout; + protected EntityPlayerModel localPlayer; protected EntityPlayerModel remotePlayer; @@ -194,8 +198,10 @@ public class GuiSkins extends GuiScreen { this.buttonList.add(this.btnModeSkin = new GuiItemStackButton(4, 2, 2, skin)); skin = new ItemStack(Items.LEATHER_LEGGINGS); Items.LEATHER_LEGGINGS.setColor(skin, 0xfff500); - this.buttonList.add(this.btnModeSkinnySkin = new GuiItemStackButton(6, 2, 21, skin)); this.buttonList.add(this.btnModeElytra = new GuiItemStackButton(5, 2, 52, new ItemStack(Items.ELYTRA))); + this.buttonList.add(this.btnModeSkinnySkin = new GuiItemStackButton(6, 2, 21, skin)); + + this.buttonList.add(this.btnAbout = new GuiButton(-1, this.width - 25, this.height - 25, 20, 20, "?")); this.btnUpload.enabled = false; this.btnBrowse.enabled = !this.mc.isFullScreen(); @@ -203,6 +209,7 @@ public class GuiSkins extends GuiScreen { this.btnModeSkin.enabled = this.thinArmType; this.btnModeSkinnySkin.enabled = !this.thinArmType; this.btnModeElytra.enabled = this.textureType == SKIN; + } private void enableDnd() { @@ -441,6 +448,10 @@ public class GuiSkins extends GuiScreen { } this.drawHoveringText(I18n.format(text), mouseX, y); } + if (this.btnAbout.isMouseOver()) { + SkinServer gateway = HDSkinManager.INSTANCE.getGatewayServer(); + this.drawHoveringText(Splitter.on("\r\n").splitToList(gateway.toString()), mouseX, mouseY); + } if (this.fetchingSkin) { String opacity1; diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/mod/LiteModHDSkinsMod.java b/src/hdskins/java/com/voxelmodpack/hdskins/mod/LiteModHDSkinsMod.java index 531ff5cb..21986d22 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/mod/LiteModHDSkinsMod.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/mod/LiteModHDSkinsMod.java @@ -1,7 +1,9 @@ package com.voxelmodpack.hdskins.mod; +import com.google.gson.GsonBuilder; import com.google.gson.annotations.Expose; import com.mumfrey.liteloader.core.LiteLoader; +import com.mumfrey.liteloader.modconfig.AdvancedExposable; import com.mumfrey.liteloader.modconfig.ConfigPanel; import com.mumfrey.liteloader.modconfig.ConfigStrategy; import com.mumfrey.liteloader.modconfig.ExposableOptions; @@ -9,26 +11,25 @@ import com.mumfrey.liteloader.util.ModUtilities; import com.voxelmodpack.hdskins.HDSkinManager; import com.voxelmodpack.hdskins.gui.EntityPlayerModel; import com.voxelmodpack.hdskins.gui.GLWindow; -import com.voxelmodpack.hdskins.gui.GuiSkins; import com.voxelmodpack.hdskins.gui.HDSkinsConfigPanel; import com.voxelmodpack.hdskins.gui.RenderPlayerModel; import com.voxelmodpack.hdskins.skins.SkinServer; +import com.voxelmodpack.hdskins.skins.SkinServerSerializer; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.resources.IReloadableResourceManager; import java.io.File; -import java.lang.reflect.Method; import java.util.List; @ExposableOptions(strategy = ConfigStrategy.Unversioned, filename = "hdskins") -public class LiteModHDSkinsMod implements HDSkinsMod { +public class LiteModHDSkinsMod implements HDSkinsMod, AdvancedExposable { @Expose - public List skin_servers = SkinServer.defaultServers; + public List skin_servers = SkinServer.defaultServers; @Expose - public boolean experimentalSkinDrop = true; + public boolean experimentalSkinDrop = false; @Override public String getName() { @@ -46,17 +47,6 @@ public class LiteModHDSkinsMod implements HDSkinsMod { // register config LiteLoader.getInstance().registerExposable(this, null); - // try it initialize voxelmenu button - try { - Class ex = Class.forName("com.thevoxelbox.voxelmenu.GuiMainMenuVoxelBox"); - Method mRegisterCustomScreen = ex.getDeclaredMethod("registerCustomScreen", Class.class, String.class); - mRegisterCustomScreen.invoke(null, GuiSkins.class, "HD Skins Manager"); - } catch (ClassNotFoundException var4) { - // voxelmenu's not here, man - } catch (Exception var5) { - var5.printStackTrace(); - } - IReloadableResourceManager irrm = (IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager(); irrm.registerReloadListener(HDSkinManager.INSTANCE); @@ -70,6 +60,16 @@ public class LiteModHDSkinsMod implements HDSkinsMod { HDSkinManager.clearSkinCache(); } + @Override + public void setupGsonSerialiser(GsonBuilder gsonBuilder) { + gsonBuilder.registerTypeAdapter(SkinServer.class, new SkinServerSerializer()); + } + + @Override + public File getConfigFile(File configFile, File configFileLocation, String defaultFileName) { + return null; + } + @Override public Class getConfigPanelClass() { return HDSkinsConfigPanel.class; @@ -80,14 +80,7 @@ public class LiteModHDSkinsMod implements HDSkinsMod { ModUtilities.addRenderer(EntityPlayerModel.class, new RenderPlayerModel<>(minecraft.getRenderManager())); // register skin servers. - for (String s : skin_servers) { - try { - HDSkinManager.INSTANCE.addSkinServer(SkinServer.from(s)); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - + skin_servers.forEach(HDSkinManager.INSTANCE::addSkinServer); } @Override diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/mod/package-info.java b/src/hdskins/java/com/voxelmodpack/hdskins/mod/package-info.java deleted file mode 100644 index 71bed627..00000000 --- a/src/hdskins/java/com/voxelmodpack/hdskins/mod/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -package com.voxelmodpack.hdskins.mod; - -import mcp.MethodsReturnNonnullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/IndentedToStringStyle.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/IndentedToStringStyle.java new file mode 100644 index 00000000..150f8d40 --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/IndentedToStringStyle.java @@ -0,0 +1,18 @@ +package com.voxelmodpack.hdskins.skins; + +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class IndentedToStringStyle extends ToStringStyle { + + public static final ToStringStyle INSTANCE = new IndentedToStringStyle(); + + private IndentedToStringStyle() { + this.setContentStart(null); + this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " "); + this.setFieldSeparatorAtStart(true); + this.setContentEnd(null); + this.setUseIdentityHashCode(false); + this.setUseShortClassName(true); + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java index c31a7c66..a15b4557 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/LegacySkinServer.java @@ -1,8 +1,8 @@ package com.voxelmodpack.hdskins.skins; -import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.Expose; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.minecraft.MinecraftProfileTexture; @@ -13,6 +13,7 @@ import com.voxelmodpack.hdskins.HDSkinManager; import com.voxelmodpack.hdskins.upload.ThreadMultipartPostUpload; import net.minecraft.client.Minecraft; import net.minecraft.util.Session; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -26,24 +27,21 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.annotation.Nullable; +@ServerType("legacy") public class LegacySkinServer implements SkinServer { private static final String SERVER_ID = "7853dfddc358333843ad55a2c7485c4aa0380a51"; private static final Logger logger = LogManager.getLogger(); + @Expose private final String address; + @Expose private final String gateway; - public LegacySkinServer(String address) { - this(address, null); - } - public LegacySkinServer(String address, @Nullable String gateway) { this.address = address; this.gateway = gateway; @@ -144,24 +142,11 @@ public class LegacySkinServer implements SkinServer { service.joinServer(session.getProfile(), session.getToken(), serverId); } - /** - * Should be in the format {@code legacy:http://address;http://gateway}. Gateway is optional. - */ - static LegacySkinServer from(String parsed) { - Matcher matcher = Pattern.compile("^legacy:(.+?)(?:;(.*))?$").matcher(parsed); - if (matcher.find()) { - String addr = matcher.group(1); - String gate = matcher.group(2); - return new LegacySkinServer(addr, gate); - } - throw new IllegalArgumentException("server format string was not correct"); - } - @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("address", address) - .add("gateway", gateway) - .toString(); + return new ToStringBuilder(this, IndentedToStringStyle.INSTANCE) + .append("address", this.address) + .append("gateway", this.gateway) + .build(); } } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ServerType.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ServerType.java new file mode 100644 index 00000000..3f7df569 --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ServerType.java @@ -0,0 +1,13 @@ +package com.voxelmodpack.hdskins.skins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ServerType { + + String value(); +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java index 142e7840..6310aba6 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServer.java @@ -17,7 +17,9 @@ import javax.annotation.Nullable; public interface SkinServer { - List defaultServers = Lists.newArrayList("legacy:http://skins.voxelmodpack.com;http://skinmanager.voxelmodpack.com"); + List defaultServers = Lists.newArrayList(new LegacySkinServer( + "http://skins.voxelmodpack.com", + "http://skinmanager.voxelmodpack.com")); Optional loadProfileData(GameProfile profile); @@ -28,18 +30,4 @@ public interface SkinServer { CompletableFuture uploadSkin(Session session, @Nullable URI image, MinecraftProfileTexture.Type type, Map metadata); - static SkinServer from(String server) { - int i = server.indexOf(':'); - if (i >= 0) { - String type = server.substring(0, i); - switch (type) { - case "legacy": - return LegacySkinServer.from(server); - case "valhalla": { - return ValhallaSkinServer.from(server); - } - } - } - throw new IllegalArgumentException(); - } } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServerSerializer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServerSerializer.java new file mode 100644 index 00000000..42f9139e --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/SkinServerSerializer.java @@ -0,0 +1,34 @@ +package com.voxelmodpack.hdskins.skins; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +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.Type; + +public class SkinServerSerializer implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(SkinServer src, Type typeOfSrc, JsonSerializationContext context) { + ServerType serverType = src.getClass().getAnnotation(ServerType.class); + if (serverType == null) { + throw new JsonIOException("Skin server class did not have a type: " + typeOfSrc); + } + JsonObject obj = context.serialize(src).getAsJsonObject(); + obj.addProperty("type", serverType.value()); + return obj; + } + + @Override + public SkinServer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + String type = json.getAsJsonObject().get("type").getAsString(); + Class clas = HDSkinManager.INSTANCE.getSkinServerClass(type); + return context.deserialize(json, clas); + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java index e1e92470..ffd4c8c4 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/skins/ValhallaSkinServer.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import com.google.gson.annotations.Expose; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.minecraft.MinecraftProfileTexture; @@ -12,6 +13,7 @@ import com.mojang.util.UUIDTypeAdapter; import com.voxelmodpack.hdskins.HDSkinManager; import net.minecraft.client.Minecraft; import net.minecraft.util.Session; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; @@ -36,22 +38,22 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.annotation.Nullable; +@ServerType("valhalla") public class ValhallaSkinServer implements SkinServer { - private final String baseURL; + @Expose + private final String address; private final Gson gson = new GsonBuilder() .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) .create(); - private String accessToken; + private transient String accessToken; - private ValhallaSkinServer(String baseURL) { - this.baseURL = baseURL; + public ValhallaSkinServer(String address) { + this.address = address; } @Override @@ -189,28 +191,27 @@ public class ValhallaSkinServer implements SkinServer { private URI buildUserTextureUri(GameProfile profile, MinecraftProfileTexture.Type textureType) { String user = UUIDTypeAdapter.fromUUID(profile.getId()); String skinType = textureType.name().toLowerCase(Locale.US); - return URI.create(String.format("%s/user/%s/%s", this.baseURL, user, skinType)); + return URI.create(String.format("%s/user/%s/%s", this.address, user, skinType)); } private URI getTexturesURI(GameProfile profile) { Preconditions.checkNotNull(profile.getId(), "profile id required for skins"); - return URI.create(String.format("%s/user/%s", this.baseURL, UUIDTypeAdapter.fromUUID(profile.getId()))); + return URI.create(String.format("%s/user/%s", this.address, UUIDTypeAdapter.fromUUID(profile.getId()))); } private URI getHandshakeURI() { - return URI.create(String.format("%s/auth/handshake", this.baseURL)); + return URI.create(String.format("%s/auth/handshake", this.address)); } private URI getResponseURI() { - return URI.create(String.format("%s/auth/response", this.baseURL)); + return URI.create(String.format("%s/auth/response", this.address)); } - static ValhallaSkinServer from(String server) { - Matcher matcher = Pattern.compile("^valhalla:(.*)$").matcher(server); - if (matcher.find()) { - return new ValhallaSkinServer(matcher.group(1)); - } - throw new IllegalArgumentException(); + @Override + public String toString() { + return new ToStringBuilder(this, IndentedToStringStyle.INSTANCE) + .append("address", this.address) + .toString(); } @SuppressWarnings("WeakerAccess") diff --git a/src/main/java/com/minelittlepony/MineLittlePony.java b/src/main/java/com/minelittlepony/MineLittlePony.java index f7d84a6d..a112e01f 100644 --- a/src/main/java/com/minelittlepony/MineLittlePony.java +++ b/src/main/java/com/minelittlepony/MineLittlePony.java @@ -7,6 +7,7 @@ import com.minelittlepony.pony.data.PonyDataSerialzier; import com.minelittlepony.render.PonySkullRenderer; import com.mumfrey.liteloader.core.LiteLoader; import com.voxelmodpack.hdskins.HDSkinManager; +import com.voxelmodpack.hdskins.skins.LegacySkinServer; import com.voxelmodpack.hdskins.skins.SkinServer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.entity.RenderManager; @@ -27,7 +28,8 @@ public class MineLittlePony { public static final String MOD_NAME = "Mine Little Pony"; public static final String MOD_VERSION = "@VERSION@"; - private static final String MINELP_LEGACY_SERVER = "legacy:http://minelpskins.voxelmodpack.com;http://minelpskinmanager.voxelmodpack.com"; + private static final String MINELP_LEGACY_SERVER = "http://minelpskins.voxelmodpack.com"; + private static final String MINELP_LEGACY_GATEWAY = "http://minelpskinmanager.voxelmodpack.com"; private static final KeyBinding SETTINGS_GUI = new KeyBinding("Settings", Keyboard.KEY_F9, "Mine Little Pony"); @@ -57,7 +59,7 @@ public class MineLittlePony { ms.registerMetadataSectionType(new PonyDataSerialzier(), IPonyData.class); // This also makes it the default gateway server. - SkinServer.defaultServers.add(MINELP_LEGACY_SERVER); + SkinServer.defaultServers.add(new LegacySkinServer(MINELP_LEGACY_SERVER, MINELP_LEGACY_GATEWAY)); } /**