mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2025-02-13 16:24:23 +01:00
Merge pull request #74 from MineLittlePony/valhalla
Valhalla Implementation
This commit is contained in:
commit
8e663c81d3
19 changed files with 564 additions and 177 deletions
|
@ -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/
|
||||
|
|
34
build.gradle
34
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'
|
||||
|
@ -44,6 +46,13 @@ sourceSets {
|
|||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// use the same version as httpclient
|
||||
compile('org.apache.httpcomponents:httpmime:4.3.2') {
|
||||
transitive = false
|
||||
}
|
||||
}
|
||||
|
||||
litemod {
|
||||
doFirst {
|
||||
json {
|
||||
|
@ -56,7 +65,6 @@ litemod {
|
|||
description.hdskinsmod = '''\
|
||||
Separate skin server for Mine Little Pony that also supports HD skins.
|
||||
Access via button on the main menu.'''
|
||||
|
||||
mixinConfigs += [
|
||||
'minelp.mixin.json',
|
||||
'hdskins.mixin.json'
|
||||
|
@ -68,6 +76,24 @@ litemod {
|
|||
jar {
|
||||
from sourceSets.hdskins.output
|
||||
from litemod
|
||||
classifier 'base'
|
||||
extension 'jar'
|
||||
}
|
||||
shadowJar {
|
||||
extension 'litemod'
|
||||
classifier "mc$minecraft.version"
|
||||
baseName "mod-${project.name.toLowerCase()}"
|
||||
|
||||
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
|
||||
|
@ -86,10 +112,16 @@ task skinZip(type: Zip) {
|
|||
version 'v1'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
||||
|
||||
reobf {
|
||||
srgJar {
|
||||
mappingType = 'SEARGE'
|
||||
}
|
||||
shadowJar {}
|
||||
|
||||
}
|
||||
mixin {
|
||||
defaultObfuscationEnv notch
|
||||
|
|
|
@ -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<ISkinCacheClearListener> clearListeners = Lists.newArrayList();
|
||||
|
||||
private BiMap<String, Class<? extends SkinServer>> skinServerTypes = HashBiMap.create(2);
|
||||
private List<SkinServer> skinServers = Lists.newArrayList();
|
||||
|
||||
private Map<UUID, Map<Type, ResourceLocation>> skinCache = Maps.newHashMap();
|
||||
|
@ -81,10 +89,17 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
private List<ISkinModifier> skinModifiers = Lists.newArrayList();
|
||||
|
||||
private SkinResourceManager resources = new SkinResourceManager();
|
||||
// private ExecutorService executor = Executors.newCachedThreadPool();
|
||||
// private ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
private Class<? extends GuiSkins> skinsClass = null;
|
||||
|
||||
private HDSkinManager() {
|
||||
|
||||
// register default skin server types
|
||||
addSkinServerType(LegacySkinServer.class);
|
||||
addSkinServerType(ValhallaSkinServer.class);
|
||||
}
|
||||
|
||||
public void setPrefferedSkinsGuiClass(Class<? extends GuiSkins> clazz) {
|
||||
skinsClass = clazz;
|
||||
}
|
||||
|
@ -102,35 +117,42 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
}
|
||||
|
||||
public Optional<ResourceLocation> 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<MinecraftTexturesPayload> 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<? extends SkinServer> 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<? extends SkinServer> 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);
|
||||
}
|
||||
|
@ -223,32 +261,8 @@ 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);
|
||||
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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures;
|
||||
|
||||
PreviewTextureManager(Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ 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.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;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
|
@ -37,13 +37,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<SkinUploadResponse> {
|
||||
private static final int MAX_SKIN_DIMENSION = 1024;
|
||||
private int updateCounter = 0;
|
||||
|
||||
|
@ -55,6 +59,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
private GuiButton btnModeSkinnySkin;
|
||||
private GuiButton btnModeElytra;
|
||||
|
||||
private GuiButton btnAbout;
|
||||
|
||||
protected EntityPlayerModel localPlayer;
|
||||
protected EntityPlayerModel remotePlayer;
|
||||
|
||||
|
@ -86,7 +92,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
|
||||
public GuiSkins() {
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
// this.screenTitle = manager;
|
||||
// this.screenTitle = manager;
|
||||
GameProfile profile = minecraft.getSession().getProfile();
|
||||
|
||||
this.localPlayer = getModel(profile);
|
||||
|
@ -192,8 +198,10 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
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();
|
||||
|
@ -201,6 +209,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
this.btnModeSkin.enabled = this.thinArmType;
|
||||
this.btnModeSkinnySkin.enabled = !this.thinArmType;
|
||||
this.btnModeElytra.enabled = this.textureType == SKIN;
|
||||
|
||||
}
|
||||
|
||||
private void enableDnd() {
|
||||
|
@ -439,6 +448,10 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
}
|
||||
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;
|
||||
|
@ -490,7 +503,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
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();
|
||||
pushMatrix();
|
||||
translate(xPosition, yPosition, 300.0F);
|
||||
|
@ -551,38 +565,44 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
|
|||
private void clearUploadedSkin(Session session) {
|
||||
this.uploadingSkin = true;
|
||||
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) {
|
||||
this.uploadingSkin = true;
|
||||
this.skinUploadMessage = I18n.format("hdskins.upload");
|
||||
Path path = skinFile == null ? null : skinFile.toPath();
|
||||
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, path, this.textureType, this.thinArmType), this);
|
||||
URI path = skinFile == null ? null : skinFile.toURI();
|
||||
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) {
|
||||
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;
|
||||
|
|
|
@ -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<String> skin_servers = SkinServer.defaultServers;
|
||||
public List<SkinServer> 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<? extends ConfigPanel> 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
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
@MethodsReturnNonnullByDefault
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.voxelmodpack.hdskins.mod;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,10 +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.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
|
@ -15,43 +13,50 @@ 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MinecraftProfileTexture> 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<MinecraftProfileTexture.Type, MinecraftProfileTexture> getPreviewTextures(GameProfile profile) {
|
||||
if (Strings.isNullOrEmpty(this.gateway)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> 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
|
||||
|
@ -88,19 +93,25 @@ public class LegacySkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@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))
|
||||
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<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, (thinSkinType ? "slim" : "default"), image);
|
||||
String model = metadata.getOrDefault("model", "default");
|
||||
Map<String, ?> 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<String, ?> getData(Session session, MinecraftProfileTexture.Type type, String model, String param, Object val) {
|
||||
|
@ -116,7 +127,7 @@ public class LegacySkinServer implements SkinServer {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -131,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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -1,39 +1,33 @@
|
|||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface SkinServer {
|
||||
|
||||
List<String> defaultServers = Lists.newArrayList("legacy:http://skins.voxelmodpack.com;http://skinmanager.voxelmodpack.com");
|
||||
List<SkinServer> defaultServers = Lists.newArrayList(new LegacySkinServer(
|
||||
"http://skins.voxelmodpack.com",
|
||||
"http://skinmanager.voxelmodpack.com"));
|
||||
|
||||
Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile);
|
||||
|
||||
Optional<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile);
|
||||
|
||||
ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType);
|
||||
|
||||
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();
|
||||
default Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getPreviewTextures(GameProfile profile) {
|
||||
return loadProfileData(profile).map(MinecraftTexturesPayload::getTextures).orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image,
|
||||
MinecraftProfileTexture.Type type, Map<String, String> metadata);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<SkinServer>, JsonDeserializer<SkinServer> {
|
||||
|
||||
@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<? extends SkinServer> clas = HDSkinManager.INSTANCE.getSkinServerClass(type);
|
||||
return context.deserialize(json, clas);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,231 @@
|
|||
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.google.gson.annotations.Expose;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.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;
|
||||
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.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ServerType("valhalla")
|
||||
public class ValhallaSkinServer implements SkinServer {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String baseURL;
|
||||
@Expose
|
||||
private final String address;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||
.create();
|
||||
|
||||
public ValhallaSkinServer(String baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
private transient String accessToken;
|
||||
|
||||
public ValhallaSkinServer(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MinecraftTexturesPayload> 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<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) {
|
||||
return null;
|
||||
public CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image,
|
||||
MinecraftProfileTexture.Type type, Map<String, String> 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);
|
||||
}
|
||||
|
||||
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<String, String> metadata) throws IOException {
|
||||
MultipartEntityBuilder b = MultipartEntityBuilder.create();
|
||||
b.addBinaryBody("file", file, ContentType.create("image/png"), file.getName());
|
||||
metadata.forEach(b::addTextBody);
|
||||
|
||||
return upload(client, RequestBuilder.put()
|
||||
.setUri(buildUserTextureUri(profile, type))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.setEntity(b.build())
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadUrl(CloseableHttpClient client, URI uri, GameProfile profile, MinecraftProfileTexture.Type type,
|
||||
Map<String, String> metadata) throws IOException {
|
||||
|
||||
return upload(client, RequestBuilder.post()
|
||||
.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> T readJson(InputStream in, Class<T> 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.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.address, UUIDTypeAdapter.fromUUID(profile.getId())));
|
||||
}
|
||||
|
||||
private URI getHandshakeURI() {
|
||||
return URI.create(String.format("%s/auth/handshake", this.address));
|
||||
}
|
||||
|
||||
private URI getResponseURI() {
|
||||
return URI.create(String.format("%s/auth/response", this.address));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType) {
|
||||
return null;
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, IndentedToStringStyle.INSTANCE)
|
||||
.append("address", this.address)
|
||||
.toString();
|
||||
}
|
||||
|
||||
static ValhallaSkinServer from(String server) {
|
||||
Matcher matcher = Pattern.compile("^valhalla:(.*)$").matcher(server);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, ?> sourceData;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue