mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-26 06:18:00 +01:00
First touches on multiple skin server support.
This commit is contained in:
parent
056c0d8baa
commit
18980c2ca2
16 changed files with 527 additions and 214 deletions
|
@ -20,7 +20,7 @@ apply plugin: 'net.minecraftforge.gradle.liteloader'
|
||||||
apply plugin: 'org.spongepowered.mixin'
|
apply plugin: 'org.spongepowered.mixin'
|
||||||
|
|
||||||
group = 'com.minelittlepony'
|
group = 'com.minelittlepony'
|
||||||
version = '1.12.2.1'
|
version = '1.12.2.2-SNAPSHOT'
|
||||||
description = 'Mine Little Pony'
|
description = 'Mine Little Pony'
|
||||||
|
|
||||||
targetCompatibility = 1.8
|
targetCompatibility = 1.8
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#Build Number for ANT. Do not edit!
|
#Build Number for ANT. Do not edit!
|
||||||
#Fri Oct 06 13:17:50 EDT 2017
|
#Tue Jan 02 00:10:52 EST 2018
|
||||||
build.number=488
|
build.number=495
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
package com.voxelmodpack.hdskins;
|
package com.voxelmodpack.hdskins;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.GsonBuilder;
|
||||||
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.minecraft.MinecraftProfileTexture.Type;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
import com.mojang.authlib.properties.Property;
|
import com.mojang.authlib.properties.Property;
|
||||||
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||||
import com.mojang.util.UUIDTypeAdapter;
|
import com.mojang.util.UUIDTypeAdapter;
|
||||||
import com.mumfrey.liteloader.core.LiteLoader;
|
import com.mumfrey.liteloader.core.LiteLoader;
|
||||||
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
||||||
import com.voxelmodpack.hdskins.resource.SkinResourceManager;
|
import com.voxelmodpack.hdskins.resource.SkinResourceManager;
|
||||||
|
import com.voxelmodpack.hdskins.skins.LegacySkinServer;
|
||||||
|
import com.voxelmodpack.hdskins.skins.SkinServer;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.IImageBuffer;
|
import net.minecraft.client.renderer.IImageBuffer;
|
||||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||||
|
@ -31,6 +32,7 @@ import java.awt.Graphics;
|
||||||
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.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -44,12 +46,13 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
public static final HDSkinManager INSTANCE = new HDSkinManager();
|
public static final HDSkinManager INSTANCE = new HDSkinManager();
|
||||||
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
|
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
|
||||||
|
private static final Gson GSON = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||||
|
.create();
|
||||||
|
|
||||||
private String gatewayUrl = "skinmanager.voxelmodpack.com";
|
private List<SkinServer> skinServers = Lists.newArrayList();
|
||||||
private String skinUrl = "skins.voxelmodpack.com";
|
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
private Map<UUID, Map<Type, MinecraftProfileTexture>> profileTextures = Maps.newHashMap();
|
|
||||||
private Map<UUID, Map<Type, ResourceLocation>> skinCache = Maps.newHashMap();
|
private Map<UUID, Map<Type, ResourceLocation>> skinCache = Maps.newHashMap();
|
||||||
private List<ISkinModifier> skinModifiers = Lists.newArrayList();
|
private List<ISkinModifier> skinModifiers = Lists.newArrayList();
|
||||||
|
|
||||||
|
@ -57,6 +60,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
private ExecutorService executor = Executors.newCachedThreadPool();
|
private ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
public HDSkinManager() {
|
public HDSkinManager() {
|
||||||
|
addSkinServer(new LegacySkinServer("http://skins.voxelmodpack.com", "http://skinmanager.voxelmodpack.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ResourceLocation> getSkinLocation(GameProfile profile1, final Type type, boolean loadIfAbsent) {
|
public Optional<ResourceLocation> getSkinLocation(GameProfile profile1, final Type type, boolean loadIfAbsent) {
|
||||||
|
@ -69,21 +73,16 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
// try to recreate a broken gameprofile
|
// try to recreate a broken gameprofile
|
||||||
// happens when server sends a random profile with skin and displayname
|
// happens when server sends a random profile with skin and displayname
|
||||||
Property prop = Iterables.getFirst(profile1.getProperties().get("textures"), null);
|
Property textures = Iterables.getFirst(profile1.getProperties().get("textures"), null);
|
||||||
if (prop != null && Strings.isNullOrEmpty(prop.getValue())) {
|
if (textures != null) {
|
||||||
JsonObject obj = new Gson().fromJson(new String(Base64.decodeBase64(prop.getValue())), JsonObject.class);
|
MinecraftTexturesPayload texturePayload = GSON.fromJson(new String(Base64.decodeBase64(textures.getValue())), MinecraftTexturesPayload.class);
|
||||||
// why are plugins sending a json null?
|
if (texturePayload != null) {
|
||||||
if (obj != null) {
|
// name is optional
|
||||||
String name = null;
|
String name = texturePayload.getProfileName();
|
||||||
// this should be optional
|
UUID uuid = texturePayload.getProfileId();
|
||||||
if (obj.has("profileName")) {
|
// uuid is required
|
||||||
name = obj.get("profileName").getAsString();
|
if (uuid != null)
|
||||||
}
|
|
||||||
// this is required
|
|
||||||
if (obj.has("profileId")) {
|
|
||||||
UUID uuid = UUIDTypeAdapter.fromString(obj.get("profileId").getAsString());
|
|
||||||
profile1 = new GameProfile(uuid, name);
|
profile1 = new GameProfile(uuid, name);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final GameProfile profile = profile1;
|
final GameProfile profile = profile1;
|
||||||
|
@ -94,7 +93,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
skin = this.skinCache.get(profile.getId()).get(type);
|
skin = this.skinCache.get(profile.getId()).get(type);
|
||||||
if (skin == null) {
|
if (skin == null) {
|
||||||
if (loadIfAbsent) {
|
if (loadIfAbsent && getProfileData(profile).containsKey(type)) {
|
||||||
skinCache.get(profile.getId()).put(type, LOADING);
|
skinCache.get(profile.getId()).put(type, LOADING);
|
||||||
//noinspection Convert2Lambda
|
//noinspection Convert2Lambda
|
||||||
executor.submit(() -> loadTexture(profile, type, new SkinAvailableCallback() {
|
executor.submit(() -> loadTexture(profile, type, new SkinAvailableCallback() {
|
||||||
|
@ -103,17 +102,16 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
skinCache.get(profile.getId()).put(type1, location);
|
skinCache.get(profile.getId()).put(type1, location);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return skin == LOADING ? Optional.empty() : Optional.of(skin);
|
return skin == LOADING ? Optional.empty() : Optional.of(skin);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadTexture(GameProfile profile, final Type type, final SkinAvailableCallback callback) {
|
private void loadTexture(GameProfile profile, final Type type, final SkinAvailableCallback callback) {
|
||||||
if (profile.getId() != null) {
|
if (profile.getId() != null) {
|
||||||
Map<Type, MinecraftProfileTexture> data = loadProfileData(profile);
|
Map<Type, MinecraftProfileTexture> data = getProfileData(profile);
|
||||||
final MinecraftProfileTexture texture = data.get(type);
|
final MinecraftProfileTexture texture = data.get(type);
|
||||||
if (texture == null) {
|
if (texture == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -150,51 +148,24 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Map<Type, MinecraftProfileTexture>> getProfileData(GameProfile profile) {
|
public Map<Type, MinecraftProfileTexture> getProfileData(GameProfile profile) {
|
||||||
if (!enabled)
|
EnumMap<Type, MinecraftProfileTexture> textures = Maps.newEnumMap(Type.class);
|
||||||
return Optional.of(ImmutableMap.of());
|
for (SkinServer server : skinServers) {
|
||||||
return Optional.ofNullable(this.profileTextures.get(profile.getId()));
|
Optional<MinecraftTexturesPayload> profileData = server.getProfileData(profile);
|
||||||
|
profileData.map(MinecraftTexturesPayload::getTextures).ifPresent(it -> it.forEach(textures::putIfAbsent));
|
||||||
|
if (textures.size() == Type.values().length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return textures;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Type, MinecraftProfileTexture> loadProfileData(final GameProfile profile) {
|
public void addSkinServer(SkinServer skinServer) {
|
||||||
return getProfileData(profile).orElseGet(() -> {
|
this.skinServers.add(0, skinServer);
|
||||||
|
|
||||||
String uuid = UUIDTypeAdapter.fromUUID(profile.getId());
|
|
||||||
|
|
||||||
ImmutableMap.Builder<Type, MinecraftProfileTexture> builder = ImmutableMap.builder();
|
|
||||||
for (Type type : Type.values()) {
|
|
||||||
String url = getCustomTextureURLForId(type, uuid);
|
|
||||||
|
|
||||||
builder.put(type, new MinecraftProfileTexture(url, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<Type, MinecraftProfileTexture> textures = builder.build();
|
|
||||||
this.profileTextures.put(profile.getId(), textures);
|
|
||||||
return textures;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSkinUrl(String skinUrl) {
|
public SkinServer getGatewayServer() {
|
||||||
this.skinUrl = skinUrl;
|
return this.skinServers.get(0);
|
||||||
}
|
|
||||||
|
|
||||||
public void setGatewayURL(String gatewayURL) {
|
|
||||||
this.gatewayUrl = gatewayURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGatewayUrl() {
|
|
||||||
return String.format("http://%s/", gatewayUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCustomTextureURLForId(Type type, String uuid, boolean gateway) {
|
|
||||||
String server = gateway ? gatewayUrl : skinUrl;
|
|
||||||
String path = type.toString().toLowerCase() + "s";
|
|
||||||
return String.format("http://%s/%s/%s.png", server, path, uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCustomTextureURLForId(Type type, String uuid) {
|
|
||||||
return getCustomTextureURLForId(type, uuid, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
|
@ -203,9 +174,9 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
public static PreviewTexture getPreviewTexture(ResourceLocation skinResource, GameProfile profile, Type type, ResourceLocation def, @Nullable final SkinAvailableCallback callback) {
|
public static PreviewTexture getPreviewTexture(ResourceLocation skinResource, GameProfile profile, Type type, ResourceLocation def, @Nullable final SkinAvailableCallback callback) {
|
||||||
TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
|
TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
|
||||||
String url = INSTANCE.getCustomTextureURLForId(type, UUIDTypeAdapter.fromUUID(profile.getId()), true);
|
MinecraftProfileTexture url = INSTANCE.getGatewayServer().getPreviewTexture(type, profile);
|
||||||
IImageBuffer buffer = new ImageBufferDownloadHD();
|
IImageBuffer buffer = new ImageBufferDownloadHD();
|
||||||
PreviewTexture skinTexture = new PreviewTexture(url, def, type == Type.SKIN ? new IImageBuffer() {
|
PreviewTexture skinTexture = new PreviewTexture(url.getUrl(), def, type == Type.SKIN ? new IImageBuffer() {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public BufferedImage parseUserSkin(BufferedImage image) {
|
public BufferedImage parseUserSkin(BufferedImage image) {
|
||||||
|
@ -215,7 +186,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
@Override
|
@Override
|
||||||
public void skinAvailable() {
|
public void skinAvailable() {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.skinAvailable(type, skinResource, new MinecraftProfileTexture(url, Maps.newHashMap()));
|
callback.skinAvailable(type, skinResource, new MinecraftProfileTexture(url.getUrl(), Maps.newHashMap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} : null);
|
} : null);
|
||||||
|
@ -235,7 +206,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
.flatMap(m -> m.values().stream())
|
.flatMap(m -> m.values().stream())
|
||||||
.forEach(textures::deleteTexture);
|
.forEach(textures::deleteTexture);
|
||||||
INSTANCE.skinCache.clear();
|
INSTANCE.skinCache.clear();
|
||||||
INSTANCE.profileTextures.clear();
|
INSTANCE.skinServers.forEach(SkinServer::clearCache);
|
||||||
} catch (IOException var1) {
|
} catch (IOException var1) {
|
||||||
var1.printStackTrace();
|
var1.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -246,8 +217,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
skinModifiers.add(modifier);
|
skinModifiers.add(modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public ResourceLocation getConvertedSkin(ResourceLocation res) {
|
||||||
public ResourceLocation getConvertedSkin(@Nullable ResourceLocation res) {
|
|
||||||
ResourceLocation loc = resources.getConvertedResource(res);
|
ResourceLocation loc = resources.getConvertedResource(res);
|
||||||
return loc == null ? res : loc;
|
return loc == null ? res : loc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +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.collect.ImmutableMap;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
|
||||||
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.upload.ThreadMultipartPostUpload;
|
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;
|
||||||
|
@ -33,6 +32,7 @@ import net.minecraft.util.Session;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.text.TextFormatting;
|
import net.minecraft.util.text.TextFormatting;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.util.glu.GLU;
|
import org.lwjgl.util.glu.GLU;
|
||||||
|
@ -44,14 +44,13 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.DoubleBuffer;
|
import java.nio.DoubleBuffer;
|
||||||
import java.util.Locale;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import javax.annotation.Nullable;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
public class GuiSkins extends GuiScreen {
|
public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResponse> {
|
||||||
private static final int MAX_SKIN_DIMENSION = 8192;
|
private static final int MAX_SKIN_DIMENSION = 1024;
|
||||||
private static final String skinServerId = "7853dfddc358333843ad55a2c7485c4aa0380a51";
|
|
||||||
private int updateCounter = 0;
|
private int updateCounter = 0;
|
||||||
private ResourceLocation viewportTexture;
|
private ResourceLocation viewportTexture;
|
||||||
private static final ResourceLocation[] cubemapTextures = {
|
private static final ResourceLocation[] cubemapTextures = {
|
||||||
|
@ -74,6 +73,7 @@ public class GuiSkins extends GuiScreen {
|
||||||
|
|
||||||
private DoubleBuffer doubleBuffer;
|
private DoubleBuffer doubleBuffer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private String uploadError;
|
private String uploadError;
|
||||||
private volatile String skinMessage = I18n.format("hdskins.choose");
|
private volatile String skinMessage = I18n.format("hdskins.choose");
|
||||||
private String skinUploadMessage = I18n.format("hdskins.request");
|
private String skinUploadMessage = I18n.format("hdskins.request");
|
||||||
|
@ -83,7 +83,6 @@ public class GuiSkins extends GuiScreen {
|
||||||
private volatile boolean throttledByMojang;
|
private volatile boolean throttledByMojang;
|
||||||
private int refreshCounter = -1;
|
private int refreshCounter = -1;
|
||||||
private ThreadOpenFilePNG openFileThread;
|
private ThreadOpenFilePNG openFileThread;
|
||||||
private ThreadMultipartPostUpload threadSkinUpload;
|
|
||||||
private final Object skinLock = new Object();
|
private final Object skinLock = new Object();
|
||||||
private File pendingSkinFile;
|
private File pendingSkinFile;
|
||||||
private File selectedSkin;
|
private File selectedSkin;
|
||||||
|
@ -606,7 +605,6 @@ public class GuiSkins extends GuiScreen {
|
||||||
Gui.drawRect(0, 0, this.width, this.height, 0xB0000000);
|
Gui.drawRect(0, 0, this.width, this.height, 0xB0000000);
|
||||||
this.drawCenteredString(this.fontRenderer, I18n.format("hdskins.failed"), this.width / 2, this.height / 2 - 10, 0xFFFFFF55);
|
this.drawCenteredString(this.fontRenderer, I18n.format("hdskins.failed"), this.width / 2, this.height / 2 - 10, 0xFFFFFF55);
|
||||||
this.drawCenteredString(this.fontRenderer, this.uploadError, this.width / 2, this.height / 2 + 2, 0xFFFF5555);
|
this.drawCenteredString(this.fontRenderer, this.uploadError, this.width / 2, this.height / 2 + 2, 0xFFFF5555);
|
||||||
LiteLoaderLogger.warning("Upload Failed: {}", this.uploadError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
depthMask(true);
|
depthMask(true);
|
||||||
|
@ -666,68 +664,43 @@ public class GuiSkins extends GuiScreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearUploadedSkin(Session session) {
|
private void clearUploadedSkin(Session session) {
|
||||||
if (this.registerServerConnection(session, skinServerId)) {
|
this.uploadingSkin = true;
|
||||||
Map<String, ?> sourceData = getClearData(session);
|
this.skinUploadMessage = I18n.format("hdskins.request");
|
||||||
this.uploadError = null;
|
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, null, this.textureType), this);
|
||||||
this.uploadingSkin = true;
|
|
||||||
this.skinUploadMessage = I18n.format("hdskins.request");
|
|
||||||
this.threadSkinUpload = new ThreadMultipartPostUpload(HDSkinManager.INSTANCE.getGatewayUrl(), sourceData, this::onUploadComplete);
|
|
||||||
this.threadSkinUpload.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadSkin(Session session, File skinFile) {
|
private void uploadSkin(Session session, @Nullable File skinFile) {
|
||||||
if (this.registerServerConnection(session, skinServerId)) {
|
this.uploadingSkin = true;
|
||||||
Map<String, ?> sourceData = getUploadData(session, skinFile);
|
this.skinUploadMessage = I18n.format("hdskins.upload");
|
||||||
this.uploadError = null;
|
Path path = skinFile == null ? null : skinFile.toPath();
|
||||||
this.uploadingSkin = true;
|
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, path, this.textureType), this);
|
||||||
this.skinUploadMessage = I18n.format("hdskins.upload");
|
|
||||||
this.threadSkinUpload = new ThreadMultipartPostUpload(HDSkinManager.INSTANCE.getGatewayUrl(), sourceData, this::onUploadComplete);
|
|
||||||
this.threadSkinUpload.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ?> getData(Session session, String param, Object val) {
|
private void setUploadError(@Nullable String error) {
|
||||||
return ImmutableMap.of(
|
this.uploadError = error != null && error.startsWith("ERROR: ") ? error.substring(7) : error;
|
||||||
"user", session.getUsername(),
|
|
||||||
"uuid", session.getPlayerID(),
|
|
||||||
"type", this.textureType.toString().toLowerCase(Locale.US),
|
|
||||||
param, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, ?> getClearData(Session session) {
|
|
||||||
return getData(session, "clear", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, ?> getUploadData(Session session, File skinFile) {
|
|
||||||
return getData(session, this.textureType.toString().toLowerCase(Locale.US), skinFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUploadError(String error) {
|
|
||||||
this.uploadError = error.startsWith("ERROR: ") ? error.substring(7) : error;
|
|
||||||
this.btnUpload.enabled = true;
|
this.btnUpload.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUploadComplete(String response) {
|
@Override
|
||||||
LiteLoaderLogger.info("Upload completed with: %s", response);
|
public void onSuccess(@Nullable SkinUploadResponse result) {
|
||||||
this.uploadingSkin = false;
|
if (result != null)
|
||||||
this.threadSkinUpload = null;
|
onUploadComplete(result);
|
||||||
if (!response.equalsIgnoreCase("OK")) {
|
|
||||||
this.setUploadError(response);
|
|
||||||
} else {
|
|
||||||
this.pendingRemoteSkinRefresh = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean registerServerConnection(Session session, String serverId) {
|
@Override
|
||||||
try {
|
public void onFailure(Throwable t) {
|
||||||
MinecraftSessionService service = Minecraft.getMinecraft().getSessionService();
|
LogManager.getLogger().warn("Upload failed", t);
|
||||||
service.joinServer(session.getProfile(), session.getToken(), serverId);
|
this.setUploadError(t.toString());
|
||||||
return true;
|
this.uploadingSkin = false;
|
||||||
} catch (AuthenticationException var4) {
|
}
|
||||||
this.setUploadError(var4.toString());
|
|
||||||
var4.printStackTrace();
|
private void onUploadComplete(SkinUploadResponse response) {
|
||||||
return false;
|
LiteLoaderLogger.info("Upload completed with: %s", response);
|
||||||
|
this.uploadingSkin = false;
|
||||||
|
if (!"OK".equalsIgnoreCase(response.getMessage())) {
|
||||||
|
this.setUploadError(response.getMessage());
|
||||||
|
} else {
|
||||||
|
this.pendingRemoteSkinRefresh = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.voxelmodpack.hdskins.mixin;
|
package com.voxelmodpack.hdskins.mixin;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||||
import net.minecraft.client.network.NetworkPlayerInfo;
|
import net.minecraft.client.network.NetworkPlayerInfo;
|
||||||
|
@ -53,16 +54,15 @@ public abstract class MixinPlayerInfo {
|
||||||
cancellable = true,
|
cancellable = true,
|
||||||
at = @At("RETURN"))
|
at = @At("RETURN"))
|
||||||
private void getSkinType(CallbackInfoReturnable<String> ci) {
|
private void getSkinType(CallbackInfoReturnable<String> ci) {
|
||||||
HDSkinManager.INSTANCE.getProfileData(getGameProfile())
|
MinecraftProfileTexture skin = HDSkinManager.INSTANCE.getProfileData(getGameProfile()).get(Type.SKIN);
|
||||||
.map(m -> m.get(Type.SKIN))
|
if (skin != null) {
|
||||||
.ifPresent(data -> {
|
String type = skin.getMetadata("model");
|
||||||
String type = data.getMetadata("model");
|
if (type == null)
|
||||||
if (type == null)
|
type = "default";
|
||||||
type = "default";
|
String type1 = type;
|
||||||
String type1 = type;
|
Optional<ResourceLocation> texture = HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), Type.SKIN, false);
|
||||||
Optional<ResourceLocation> texture = HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), Type.SKIN, false);
|
|
||||||
|
|
||||||
texture.ifPresent((res) -> ci.setReturnValue(type1));
|
texture.ifPresent((res) -> ci.setReturnValue(type1));
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public abstract class AbstractSkinServer implements SkinServer {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
protected static final ExecutorService skinDownloadExecutor = Executors.newCachedThreadPool();
|
||||||
|
protected static final ListeningExecutorService skinUploadExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
|
||||||
|
|
||||||
|
public static final MinecraftTexturesPayload EMPTY_PAYLOAD = new MinecraftTexturesPayload();
|
||||||
|
|
||||||
|
private LoadingCache<GameProfile, Optional<MinecraftTexturesPayload>> skins = CacheBuilder.newBuilder()
|
||||||
|
.initialCapacity(20)
|
||||||
|
.maximumSize(100)
|
||||||
|
.expireAfterWrite(4, TimeUnit.HOURS)
|
||||||
|
.build(AsyncCacheLoader.create(new CacheLoader<GameProfile, Optional<MinecraftTexturesPayload>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<MinecraftTexturesPayload> load(GameProfile key) {
|
||||||
|
return loadProfileData(key);
|
||||||
|
}
|
||||||
|
}, Optional.empty(), skinDownloadExecutor));
|
||||||
|
|
||||||
|
protected abstract Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Optional<MinecraftTexturesPayload> getProfileData(GameProfile profile) {
|
||||||
|
|
||||||
|
boolean was = !skins.asMap().containsKey(profile);
|
||||||
|
Optional<MinecraftTexturesPayload> textures = skins.getUnchecked(profile);
|
||||||
|
// This is the initial value. Refreshing will load it syncronously.
|
||||||
|
if (was) {
|
||||||
|
skins.refresh(profile);
|
||||||
|
}
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearCache() {
|
||||||
|
skins.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
public class AsyncCacheLoader<K, V> extends CacheLoader<K, V> {
|
||||||
|
|
||||||
|
public static <K, V> AsyncCacheLoader<K, V> create(CacheLoader<K, V> loader, V placeholder, Executor executor) {
|
||||||
|
return new AsyncCacheLoader<>(loader, placeholder, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CacheLoader<K, V> loader;
|
||||||
|
private final V placeholder;
|
||||||
|
private final Executor executor;
|
||||||
|
|
||||||
|
private AsyncCacheLoader(CacheLoader<K, V> loader, V placeholder, Executor executor) {
|
||||||
|
this.executor = executor;
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
this.loader = loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V load(K key) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<V> reload(final K key, final V oldValue) {
|
||||||
|
ListenableFutureTask<V> task = ListenableFutureTask.create(() -> loader.reload(key, oldValue).get());
|
||||||
|
executor.execute(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
|
||||||
|
return loader.loadAll(keys);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
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.voxelmodpack.hdskins.upload.ThreadMultipartPostUpload;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.util.Session;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class LegacySkinServer extends AbstractSkinServer {
|
||||||
|
|
||||||
|
private static final String SERVER_ID = "7853dfddc358333843ad55a2c7485c4aa0380a51";
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
private final String address;
|
||||||
|
private final String gateway;
|
||||||
|
|
||||||
|
public LegacySkinServer(String address, String gateway) {
|
||||||
|
this.address = address;
|
||||||
|
this.gateway = gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGateway() {
|
||||||
|
return gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MinecraftProfileTexture getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) {
|
||||||
|
return new MinecraftProfileTexture(getPath(getGateway(), type, profile), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile) {
|
||||||
|
ImmutableMap.Builder<MinecraftProfileTexture.Type, MinecraftProfileTexture> builder = ImmutableMap.builder();
|
||||||
|
for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.Type.values()) {
|
||||||
|
|
||||||
|
String url = getPath(getAddress(), type, profile);
|
||||||
|
try {
|
||||||
|
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
if (urlConnection.getResponseCode() / 100 != 2) {
|
||||||
|
throw new IOException("Bad response code: " + urlConnection.getResponseCode());
|
||||||
|
}
|
||||||
|
builder.put(type, new MinecraftProfileTexture(url, null));
|
||||||
|
logger.info("Found skin for {} at {}", profile.getName(), url);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug("Couldn't find texture at {}. Does it exist?", url, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> map = builder.build();
|
||||||
|
if (map.isEmpty()) {
|
||||||
|
logger.debug("No textures found for {} at {}", profile, this.getAddress());
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(new TexturesPayloadBuilder()
|
||||||
|
.profileId(profile.getId())
|
||||||
|
.profileName(profile.getName())
|
||||||
|
.timestamp(System.currentTimeMillis())
|
||||||
|
.isPublic(true)
|
||||||
|
.textures(map)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type) {
|
||||||
|
|
||||||
|
return skinUploadExecutor.submit(() -> {
|
||||||
|
verifyServerConnection(session, SERVER_ID);
|
||||||
|
|
||||||
|
Map<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, image);
|
||||||
|
ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(getGateway(), data);
|
||||||
|
String response = upload.uploadMultipart();
|
||||||
|
return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ?> getData(Session session, MinecraftProfileTexture.Type type, String param, Object val) {
|
||||||
|
return ImmutableMap.of(
|
||||||
|
"user", session.getUsername(),
|
||||||
|
"uuid", session.getPlayerID(),
|
||||||
|
"type", type.toString().toLowerCase(Locale.US),
|
||||||
|
param, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ?> getClearData(Session session, MinecraftProfileTexture.Type type) {
|
||||||
|
return getData(session, type, "clear", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ?> getUploadData(Session session, MinecraftProfileTexture.Type type, Path skinFile) {
|
||||||
|
return getData(session, type, type.toString().toLowerCase(Locale.US), skinFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPath(String address, MinecraftProfileTexture.Type type, GameProfile profile) {
|
||||||
|
String uuid = UUIDTypeAdapter.fromUUID(profile.getId());
|
||||||
|
String path = type.toString().toLowerCase() + "s";
|
||||||
|
return String.format("%s/%s/%s.png", address, path, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyServerConnection(Session session, String serverId) throws AuthenticationException {
|
||||||
|
MinecraftSessionService service = Minecraft.getMinecraft().getSessionService();
|
||||||
|
service.joinServer(session.getProfile(), session.getToken(), serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
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.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface SkinServer {
|
||||||
|
|
||||||
|
String getAddress();
|
||||||
|
|
||||||
|
String getGateway();
|
||||||
|
|
||||||
|
Optional<MinecraftTexturesPayload> getProfileData(GameProfile profile);
|
||||||
|
|
||||||
|
MinecraftProfileTexture getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile);
|
||||||
|
|
||||||
|
ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type);
|
||||||
|
|
||||||
|
void clearCache();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class SkinUploadResponse {
|
||||||
|
|
||||||
|
private final boolean success;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
public SkinUploadResponse(boolean success, String message) {
|
||||||
|
this.success = success;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("success", success)
|
||||||
|
.add("message", message)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||||
|
import com.mojang.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to build a {@link MinecraftTexturesPayload} object. This is
|
||||||
|
* required because it has no useful constructor. This uses reflection
|
||||||
|
* via Gson to create a new instance and populate the fields.
|
||||||
|
*/
|
||||||
|
public class TexturesPayloadBuilder {
|
||||||
|
|
||||||
|
private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
||||||
|
|
||||||
|
private long timestamp;
|
||||||
|
private UUID profileId;
|
||||||
|
private String profileName;
|
||||||
|
private boolean isPublic;
|
||||||
|
private Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures;
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder timestamp(long time) {
|
||||||
|
this.timestamp = time;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder profileId(UUID uuid) {
|
||||||
|
this.profileId = uuid;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder profileName(String name) {
|
||||||
|
this.profileName = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder isPublic(boolean pub) {
|
||||||
|
this.isPublic = pub;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder texture(MinecraftProfileTexture.Type type, MinecraftProfileTexture texture) {
|
||||||
|
if (textures == null) textures = Maps.newEnumMap(MinecraftProfileTexture.Type.class);
|
||||||
|
this.textures.put(type, texture);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturesPayloadBuilder textures(Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures) {
|
||||||
|
this.textures = textures;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MinecraftTexturesPayload build() {
|
||||||
|
return gson.fromJson(gson.toJson(this), MinecraftTexturesPayload.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
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.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class YggSkinServer extends AbstractSkinServer {
|
||||||
|
|
||||||
|
private final String baseURL;
|
||||||
|
|
||||||
|
public YggSkinServer(String baseURL) {
|
||||||
|
this.baseURL = baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAddress() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGateway() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MinecraftProfileTexture getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
@MethodsReturnNonnullByDefault
|
||||||
|
@ParametersAreNonnullByDefault
|
||||||
|
package com.voxelmodpack.hdskins.skins;
|
||||||
|
|
||||||
|
import mcp.MethodsReturnNonnullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -1,6 +0,0 @@
|
||||||
package com.voxelmodpack.hdskins.upload;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface IUploadCompleteCallback {
|
|
||||||
void onUploadComplete(String response);
|
|
||||||
}
|
|
|
@ -1,25 +1,25 @@
|
||||||
package com.voxelmodpack.hdskins.upload;
|
package com.voxelmodpack.hdskins.upload;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploader for Multipart form data
|
* Uploader for Multipart form data
|
||||||
*
|
*
|
||||||
* @author Adam Mummery-Smith
|
* @author Adam Mummery-Smith
|
||||||
*/
|
*/
|
||||||
public class ThreadMultipartPostUpload extends Thread {
|
public class ThreadMultipartPostUpload {
|
||||||
protected final Map<String, ?> sourceData;
|
protected final Map<String, ?> sourceData;
|
||||||
|
|
||||||
protected final String method;
|
protected final String method;
|
||||||
|
@ -28,8 +28,6 @@ public class ThreadMultipartPostUpload extends Thread {
|
||||||
|
|
||||||
protected final String urlString;
|
protected final String urlString;
|
||||||
|
|
||||||
protected final IUploadCompleteCallback callback;
|
|
||||||
|
|
||||||
protected HttpURLConnection httpClient;
|
protected HttpURLConnection httpClient;
|
||||||
|
|
||||||
protected static final String CRLF = "\r\n";
|
protected static final String CRLF = "\r\n";
|
||||||
|
@ -40,34 +38,18 @@ public class ThreadMultipartPostUpload extends Thread {
|
||||||
|
|
||||||
public String response;
|
public String response;
|
||||||
|
|
||||||
public ThreadMultipartPostUpload(String method, String url, Map<String, ?> sourceData, @Nullable String authorization, IUploadCompleteCallback callback) {
|
public ThreadMultipartPostUpload(String method, String url, Map<String, ?> sourceData, @Nullable String authorization) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.urlString = url;
|
this.urlString = url;
|
||||||
this.sourceData = sourceData;
|
this.sourceData = sourceData;
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
this.callback = callback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThreadMultipartPostUpload(String url, Map<String, ?> sourceData, IUploadCompleteCallback callback) {
|
public ThreadMultipartPostUpload(String url, Map<String, ?> sourceData) {
|
||||||
this("POST", url, sourceData, null, callback);
|
this("POST", url, sourceData, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResponse() {
|
public String uploadMultipart() throws IOException {
|
||||||
return this.response == null ? "" : this.response.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
this.uploadMultipart();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.callback.onUploadComplete(this.getResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void uploadMultipart() throws IOException {
|
|
||||||
// open a URL connection
|
// open a URL connection
|
||||||
URL url = new URL(this.urlString);
|
URL url = new URL(this.urlString);
|
||||||
|
|
||||||
|
@ -88,49 +70,33 @@ public class ThreadMultipartPostUpload extends Thread {
|
||||||
this.httpClient.addRequestProperty("Authorization", this.authorization);
|
this.httpClient.addRequestProperty("Authorization", this.authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataOutputStream outputStream = new DataOutputStream(this.httpClient.getOutputStream());
|
try (DataOutputStream outputStream = new DataOutputStream(this.httpClient.getOutputStream())) {
|
||||||
|
|
||||||
for (Entry<String, ?> data : this.sourceData.entrySet()) {
|
for (Entry<String, ?> data : this.sourceData.entrySet()) {
|
||||||
outputStream.writeBytes(twoHyphens + boundary + CRLF);
|
outputStream.writeBytes(twoHyphens + boundary + CRLF);
|
||||||
|
|
||||||
String paramName = data.getKey();
|
String paramName = data.getKey();
|
||||||
Object paramData = data.getValue();
|
Object paramData = data.getValue();
|
||||||
|
|
||||||
if (paramData instanceof File) {
|
if (paramData instanceof Path) {
|
||||||
File uploadFile = (File) paramData;
|
Path uploadPath = (Path) paramData;
|
||||||
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + uploadFile.getName() + "\"" + CRLF + CRLF);
|
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + uploadPath.getFileName() + "\"" + CRLF + CRLF);
|
||||||
|
|
||||||
Files.asByteSource(uploadFile).copyTo(outputStream);
|
Files.copy(uploadPath, outputStream);
|
||||||
|
} else {
|
||||||
|
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"" + CRLF + CRLF);
|
||||||
|
outputStream.writeBytes(paramData.toString());
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
outputStream.writeBytes(ThreadMultipartPostUpload.CRLF);
|
||||||
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"" + CRLF + CRLF);
|
|
||||||
outputStream.writeBytes(paramData.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.writeBytes(ThreadMultipartPostUpload.CRLF);
|
outputStream.writeBytes(twoHyphens + boundary + twoHyphens + CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.writeBytes(twoHyphens + boundary + twoHyphens + CRLF);
|
try (InputStream input = this.httpClient.getInputStream()) {
|
||||||
outputStream.flush();
|
return IOUtils.toString(input, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
InputStream httpStream = this.httpClient.getInputStream();
|
|
||||||
|
|
||||||
try {
|
|
||||||
StringBuilder readString = new StringBuilder();
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(httpStream));
|
|
||||||
|
|
||||||
String readLine;
|
|
||||||
while ((readLine = reader.readLine()) != null) {
|
|
||||||
readString.append(readLine).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.close();
|
|
||||||
this.response = readString.toString();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.mumfrey.liteloader.core.LiteLoader;
|
||||||
import com.mumfrey.liteloader.util.ModUtilities;
|
import com.mumfrey.liteloader.util.ModUtilities;
|
||||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||||
import com.voxelmodpack.hdskins.gui.GuiSkins;
|
import com.voxelmodpack.hdskins.gui.GuiSkins;
|
||||||
|
import com.voxelmodpack.hdskins.skins.LegacySkinServer;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.entity.Render;
|
import net.minecraft.client.renderer.entity.Render;
|
||||||
import net.minecraft.client.renderer.entity.RenderManager;
|
import net.minecraft.client.renderer.entity.RenderManager;
|
||||||
|
@ -89,6 +90,9 @@ public class MineLittlePony {
|
||||||
manager.addSkinModifier(new PonySkinModifier());
|
manager.addSkinModifier(new PonySkinModifier());
|
||||||
// logger.info("Set MineLP skin server URL.");
|
// logger.info("Set MineLP skin server URL.");
|
||||||
|
|
||||||
|
// This also makes it the default gateway server.
|
||||||
|
manager.addSkinServer(new LegacySkinServer("http://minelpskins.voxelmodpack.com", "http://minelpskinmanager.voxelmodpack.com"));
|
||||||
|
|
||||||
RenderManager rm = minecraft.getRenderManager();
|
RenderManager rm = minecraft.getRenderManager();
|
||||||
this.saveCurrentRenderers(rm);
|
this.saveCurrentRenderers(rm);
|
||||||
ModUtilities.addRenderer(EntityPonyModel.class, new RenderPonyModel(rm));
|
ModUtilities.addRenderer(EntityPonyModel.class, new RenderPonyModel(rm));
|
||||||
|
|
Loading…
Reference in a new issue