Update SkinServer to be more extensible.

This commit is contained in:
Matthew Messinger 2018-07-08 03:34:35 -04:00
parent a92a121a5b
commit 89ecc9f916
5 changed files with 81 additions and 38 deletions

View file

@ -4,15 +4,13 @@ import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.ELYTRA;
import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.SKIN; import static com.mojang.authlib.minecraft.MinecraftProfileTexture.Type.SKIN;
import static net.minecraft.client.renderer.GlStateManager.*; import static net.minecraft.client.renderer.GlStateManager.*;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mumfrey.liteloader.util.log.LiteLoaderLogger; import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
import com.voxelmodpack.hdskins.HDSkinManager; import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.skins.SkinUploadResponse; import com.voxelmodpack.hdskins.skins.SkinUploadResponse;
import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG; import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiButton;
@ -37,13 +35,17 @@ import org.lwjgl.opengl.GL11;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.nio.DoubleBuffer; import java.nio.DoubleBuffer;
import java.nio.file.Path; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; 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<SkinUploadResponse> {
private static final int MAX_SKIN_DIMENSION = 1024; private static final int MAX_SKIN_DIMENSION = 1024;
private int updateCounter = 0; private int updateCounter = 0;
@ -86,7 +88,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
public GuiSkins() { public GuiSkins() {
Minecraft minecraft = Minecraft.getMinecraft(); Minecraft minecraft = Minecraft.getMinecraft();
// this.screenTitle = manager; // this.screenTitle = manager;
GameProfile profile = minecraft.getSession().getProfile(); GameProfile profile = minecraft.getSession().getProfile();
this.localPlayer = getModel(profile); this.localPlayer = getModel(profile);
@ -490,7 +492,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
enableDepth(); enableDepth();
} }
private void renderPlayerModel(EntityPlayerModel thePlayer, float xPosition, float yPosition, float scale, float mouseY, float mouseX, float partialTick) { private void renderPlayerModel(EntityPlayerModel thePlayer, float xPosition, float yPosition, float scale, float mouseY, float mouseX,
float partialTick) {
enableColorMaterial(); enableColorMaterial();
pushMatrix(); pushMatrix();
translate(xPosition, yPosition, 300.0F); translate(xPosition, yPosition, 300.0F);
@ -551,38 +554,44 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
private void clearUploadedSkin(Session session) { private void clearUploadedSkin(Session session) {
this.uploadingSkin = true; this.uploadingSkin = true;
this.skinUploadMessage = I18n.format("hdskins.request"); this.skinUploadMessage = I18n.format("hdskins.request");
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, null, this.textureType, this.thinArmType), this); HDSkinManager.INSTANCE.getGatewayServer()
.uploadSkin(session, null, this.textureType, getMetadata())
.thenAccept(this::onUploadComplete)
.exceptionally(this::onFailure);
} }
private void uploadSkin(Session session, @Nullable File skinFile) { private void uploadSkin(Session session, @Nullable File skinFile) {
this.uploadingSkin = true; this.uploadingSkin = true;
this.skinUploadMessage = I18n.format("hdskins.upload"); this.skinUploadMessage = I18n.format("hdskins.upload");
Path path = skinFile == null ? null : skinFile.toPath(); URI path = skinFile == null ? null : skinFile.toURI();
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, path, this.textureType, this.thinArmType), this); HDSkinManager.INSTANCE.getGatewayServer()
.uploadSkin(session, path, this.textureType, getMetadata())
.thenAccept(this::onUploadComplete)
.exceptionally(this::onFailure);
}
private Map<String, String> getMetadata() {
return ImmutableMap.of("model", this.thinArmType ? "slim" : "default");
} }
private void setUploadError(@Nullable String error) { private void setUploadError(@Nullable String error) {
this.uploadError = error != null && error.startsWith("ERROR: ") ? error.substring(7) : error; this.uploadError = error;
this.btnUpload.enabled = true; this.btnUpload.enabled = true;
} }
@Override
public void onSuccess(@Nullable SkinUploadResponse result) {
if (result != null)
onUploadComplete(result);
}
@Override private Void onFailure(Throwable t) {
public void onFailure(Throwable t) {
LogManager.getLogger().warn("Upload failed", t); LogManager.getLogger().warn("Upload failed", t);
this.setUploadError(t.toString()); this.setUploadError(t.toString());
this.uploadingSkin = false; this.uploadingSkin = false;
return null;
} }
private void onUploadComplete(SkinUploadResponse response) { private void onUploadComplete(SkinUploadResponse response) {
LiteLoaderLogger.info("Upload completed with: %s", response); LiteLoaderLogger.info("Upload completed with: %s", response);
this.uploadingSkin = false; this.uploadingSkin = false;
if (!"OK".equalsIgnoreCase(response.getMessage())) { if (!response.isSuccess()) {
this.setUploadError(response.getMessage()); this.setUploadError(response.getMessage());
} else { } else {
this.pendingRemoteSkinRefresh = true; this.pendingRemoteSkinRefresh = true;

View file

@ -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 <T> CompletableFuture<T> asyncFailableFuture(Callable<T> call, Executor exec) {
CompletableFuture<T> ret = new CompletableFuture<>();
exec.execute(() -> {
try {
ret.complete(call.call());
} catch (Throwable e) {
ret.completeExceptionally(e);
}
});
return ret;
}
public static <T> CompletableFuture<T> failedFuture(Exception e) {
CompletableFuture<T> ret = new CompletableFuture<>();
ret.completeExceptionally(e);
return ret;
}
}

View file

@ -3,8 +3,6 @@ package com.voxelmodpack.hdskins.skins;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap; 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.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTexture;
@ -20,13 +18,15 @@ import org.apache.logging.log4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.nio.file.Path;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class LegacySkinServer implements SkinServer { public class LegacySkinServer implements SkinServer {
@ -88,19 +88,24 @@ public class LegacySkinServer implements SkinServer {
} }
@Override @Override
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinSkinType) { public CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image,
MinecraftProfileTexture.Type type, Map<String, String> metadata) {
if (Strings.isNullOrEmpty(this.gateway)) if (Strings.isNullOrEmpty(this.gateway)) {
return Futures.immediateFailedFuture(new NullPointerException("gateway url is blank")); return CallableFutures.failedFuture(new NullPointerException("gateway url is blank"));
}
return HDSkinManager.skinUploadExecutor.submit(() -> { return CallableFutures.asyncFailableFuture(() -> {
verifyServerConnection(session, SERVER_ID); verifyServerConnection(session, SERVER_ID);
String model = metadata.getOrDefault("model", "default");
Map<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, (thinSkinType ? "slim" : "default"), image); Map<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, model, image);
ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(this.gateway, data); ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(this.gateway, data);
String response = upload.uploadMultipart(); String response = upload.uploadMultipart();
if (response.startsWith("ERROR: "))
response = response.substring(7);
return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response); return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response);
});
}, HDSkinManager.skinUploadExecutor);
} }
private static Map<String, ?> getData(Session session, MinecraftProfileTexture.Type type, String model, String param, Object val) { private static Map<String, ?> 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"); return getData(session, type, "default", "clear", "1");
} }
private static Map<String, ?> getUploadData(Session session, MinecraftProfileTexture.Type type, String model, Path skinFile) { private static Map<String, ?> getUploadData(Session session, MinecraftProfileTexture.Type type, String model, URI skinFile) {
return getData(session, type, model, type.toString().toLowerCase(Locale.US), skinFile); return getData(session, type, model, type.toString().toLowerCase(Locale.US), skinFile);
} }

View file

@ -1,15 +1,17 @@
package com.voxelmodpack.hdskins.skins; package com.voxelmodpack.hdskins.skins;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload; import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import net.minecraft.util.Session; import net.minecraft.util.Session;
import java.nio.file.Path; import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public interface SkinServer { public interface SkinServer {
@ -18,9 +20,12 @@ public interface SkinServer {
Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile); Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile);
Optional<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile); default Optional<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) {
return loadProfileData(profile).map(data -> data.getTextures().get(type));
}
ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType); CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image,
MinecraftProfileTexture.Type type, Map<String, String> metadata);
static SkinServer from(String server) { static SkinServer from(String server) {
int i = server.indexOf(':'); int i = server.indexOf(':');
@ -36,4 +41,5 @@ public interface SkinServer {
} }
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
} }

View file

@ -2,8 +2,6 @@ package com.voxelmodpack.hdskins.skins;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import javax.annotation.Nullable;
public class SkinUploadResponse { public class SkinUploadResponse {
private final boolean success; private final boolean success;
@ -18,7 +16,6 @@ public class SkinUploadResponse {
return success; return success;
} }
@Nullable
public String getMessage() { public String getMessage() {
return message; return message;
} }