mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2025-02-20 11:24:23 +01:00
Backport skin server changes and remove the defaults for old legacy skin servers
This commit is contained in:
parent
1cddac4ea4
commit
1e55a7a14a
14 changed files with 455 additions and 177 deletions
|
@ -4,8 +4,6 @@ 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.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -13,6 +11,7 @@ import com.google.common.collect.Maps;
|
|||
import com.google.common.collect.Streams;
|
||||
import com.minelittlepony.gui.IconicButton;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
|
@ -20,15 +19,12 @@ import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
|||
import com.mumfrey.liteloader.core.LiteLoader;
|
||||
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
||||
import com.voxelmodpack.hdskins.ducks.INetworkPlayerInfo;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.gui.GuiSkins;
|
||||
import com.voxelmodpack.hdskins.resources.SkinResourceManager;
|
||||
import com.voxelmodpack.hdskins.resources.TextureLoader;
|
||||
import com.voxelmodpack.hdskins.resources.texture.ImageBufferDownloadHD;
|
||||
import com.voxelmodpack.hdskins.server.BethlehemSkinServer;
|
||||
import com.voxelmodpack.hdskins.server.LegacySkinServer;
|
||||
import com.voxelmodpack.hdskins.server.ServerType;
|
||||
import com.voxelmodpack.hdskins.server.SkinServer;
|
||||
import com.voxelmodpack.hdskins.server.ValhallaSkinServer;
|
||||
import com.voxelmodpack.hdskins.util.CallableFutures;
|
||||
import com.voxelmodpack.hdskins.util.MoreStreams;
|
||||
import com.voxelmodpack.hdskins.util.PlayerUtil;
|
||||
|
@ -58,7 +54,6 @@ 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;
|
||||
|
@ -88,7 +83,6 @@ 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 LoadingCache<GameProfile, CompletableFuture<Map<Type, MinecraftProfileTexture>>> skins = CacheBuilder.newBuilder()
|
||||
|
@ -103,11 +97,6 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
private Function<List<SkinServer>, GuiSkins> skinsGuiFunc = GuiSkins::new;
|
||||
|
||||
private HDSkinManager() {
|
||||
|
||||
// register default skin server types
|
||||
addSkinServerType(LegacySkinServer.class);
|
||||
addSkinServerType(ValhallaSkinServer.class);
|
||||
addSkinServerType(BethlehemSkinServer.class);
|
||||
}
|
||||
|
||||
public void setSkinsGui(Function<List<SkinServer>, GuiSkins> skinsGuiFunc) {
|
||||
|
@ -138,11 +127,13 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
|
||||
for (SkinServer server : skinServers) {
|
||||
try {
|
||||
server.loadProfileData(profile).getTextures().forEach(textureMap::putIfAbsent);
|
||||
if (textureMap.size() == Type.values().length) {
|
||||
break;
|
||||
if (!server.supportsFeature(Feature.SYNTHETIC)) {
|
||||
server.loadProfileData(profile).getTextures().forEach(textureMap::putIfAbsent);
|
||||
if (textureMap.size() == Type.values().length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | AuthenticationException e) {
|
||||
logger.trace(e);
|
||||
}
|
||||
|
||||
|
@ -231,23 +222,6 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
return map;
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
|
||||
void addSkinServer(SkinServer skinServer) {
|
||||
this.skinServers.add(skinServer);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.voxelmodpack.hdskins;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.mumfrey.liteloader.Configurable;
|
||||
|
@ -14,8 +15,7 @@ import com.mumfrey.liteloader.util.ModUtilities;
|
|||
import com.voxelmodpack.hdskins.gui.EntityPlayerModel;
|
||||
import com.voxelmodpack.hdskins.gui.HDSkinsConfigPanel;
|
||||
import com.voxelmodpack.hdskins.gui.RenderPlayerModel;
|
||||
import com.voxelmodpack.hdskins.server.SkinServer;
|
||||
import com.voxelmodpack.hdskins.server.SkinServerSerializer;
|
||||
import com.voxelmodpack.hdskins.server.*;
|
||||
import com.voxelmodpack.hdskins.upload.GLWindow;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -35,7 +35,10 @@ public class LiteModHDSkins implements InitCompleteListener, ViewportListener, C
|
|||
}
|
||||
|
||||
@Expose
|
||||
public List<SkinServer> skin_servers = SkinServer.defaultServers;
|
||||
public List<SkinServer> skin_servers = Lists.newArrayList(
|
||||
new ValhallaSkinServer("https://skins.minelittlepony-mod.com"),
|
||||
new YggdrasilSkinServer()
|
||||
);
|
||||
|
||||
@Expose
|
||||
public boolean experimentalSkinDrop = false;
|
||||
|
@ -54,7 +57,7 @@ public class LiteModHDSkins implements InitCompleteListener, ViewportListener, C
|
|||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "4.0.0";
|
||||
return "4.0.1";
|
||||
}
|
||||
|
||||
public void writeConfig() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.voxelmodpack.hdskins;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
@ -10,10 +11,9 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
||||
import com.mojang.authlib.exceptions.*;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||
import com.voxelmodpack.hdskins.gui.EntityPlayerModel;
|
||||
|
@ -33,7 +33,6 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SkinUploader implements Closeable {
|
||||
|
||||
|
@ -55,11 +54,12 @@ public class SkinUploader implements Closeable {
|
|||
|
||||
private Type skinType;
|
||||
|
||||
private Map<String, String> skinMetadata = new HashMap<String, String>();
|
||||
private Map<String, String> skinMetadata = new HashMap<>();
|
||||
|
||||
private volatile boolean fetchingSkin = false;
|
||||
private volatile boolean throttlingNeck = false;
|
||||
private volatile boolean offline = false;
|
||||
private volatile boolean pending = false;
|
||||
|
||||
private volatile boolean sendingSkin = false;
|
||||
|
||||
|
@ -78,10 +78,6 @@ public class SkinUploader implements Closeable {
|
|||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private static <T> Iterator<T> cycle(List<T> list, Predicate<T> filter) {
|
||||
return Iterables.cycle(Iterables.filter(list, filter::test)).iterator();
|
||||
}
|
||||
|
||||
public SkinUploader(List<SkinServer> servers, EntityPlayerModel local, EntityPlayerModel remote, ISkinUploadHandler listener) {
|
||||
|
||||
localPlayer = local;
|
||||
|
@ -91,7 +87,7 @@ public class SkinUploader implements Closeable {
|
|||
skinMetadata.put("model", "default");
|
||||
|
||||
this.listener = listener;
|
||||
skinServers = cycle(servers, SkinServer::verifyGateway);
|
||||
skinServers = Iterators.cycle(servers);
|
||||
cycleGateway();
|
||||
}
|
||||
|
||||
|
@ -190,17 +186,14 @@ public class SkinUploader implements Closeable {
|
|||
sendingSkin = true;
|
||||
status = statusMsg;
|
||||
|
||||
return gateway.uploadSkin(new SkinUpload(mc.getSession(), skinType, localSkin == null ? null : localSkin.toURI(), skinMetadata)).handle((response, throwable) -> {
|
||||
if (throwable == null) {
|
||||
logger.info("Upload completed with: %s", response);
|
||||
setError(null);
|
||||
} else {
|
||||
setError(Throwables.getRootCause(throwable).toString());
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
gateway.performSkinUpload(new SkinUpload(mc.getSession(), skinType, localSkin == null ? null : localSkin.toURI(), skinMetadata));
|
||||
setError("");
|
||||
} catch (IOException | AuthenticationException e) {
|
||||
handleException(e);
|
||||
}
|
||||
|
||||
fetchRemote();
|
||||
return null;
|
||||
});
|
||||
}, HDSkinManager.skinUploadExecutor).thenRunAsync(this::fetchRemote);
|
||||
}
|
||||
|
||||
public CompletableFuture<MoreHttpResponses> downloadSkin() {
|
||||
|
@ -210,42 +203,59 @@ public class SkinUploader implements Closeable {
|
|||
}
|
||||
|
||||
protected void fetchRemote() {
|
||||
boolean wasPending = pending;
|
||||
pending = false;
|
||||
fetchingSkin = true;
|
||||
throttlingNeck = false;
|
||||
offline = false;
|
||||
|
||||
remotePlayer.reloadRemoteSkin(this, (type, location, profileTexture) -> {
|
||||
fetchingSkin = false;
|
||||
if (type == skinType) {
|
||||
fetchingSkin = false;
|
||||
if (wasPending) {
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(net.minecraft.init.SoundEvents.ENTITY_VILLAGER_YES, 1));
|
||||
}
|
||||
}
|
||||
listener.onSetRemoteSkin(type, location, profileTexture);
|
||||
}).handle((a, throwable) -> {
|
||||
}).handleAsync((a, throwable) -> {
|
||||
fetchingSkin = false;
|
||||
|
||||
if (throwable != null) {
|
||||
throwable = throwable.getCause();
|
||||
|
||||
if (throwable instanceof AuthenticationUnavailableException) {
|
||||
offline = true;
|
||||
} else if (throwable instanceof AuthenticationException) {
|
||||
throttlingNeck = true;
|
||||
} else if (throwable instanceof HttpException) {
|
||||
HttpException ex = (HttpException)throwable;
|
||||
|
||||
logger.error(ex.getReasonPhrase(), ex);
|
||||
|
||||
int code = ex.getStatusCode();
|
||||
|
||||
if (code >= 500) {
|
||||
setError(String.format("A fatal server error has ocurred (check logs for details): \n%s", ex.getReasonPhrase()));
|
||||
} else if (code >= 400 && code != 403 && code != 404) {
|
||||
setError(ex.getReasonPhrase());
|
||||
}
|
||||
} else {
|
||||
logger.error("Unhandled exception", throwable);
|
||||
setError(throwable.toString());
|
||||
}
|
||||
handleException(throwable.getCause());
|
||||
} else {
|
||||
retries = 1;
|
||||
}
|
||||
return a;
|
||||
});
|
||||
}, Minecraft.getMinecraft()::addScheduledTask);
|
||||
}
|
||||
|
||||
private void handleException(Throwable throwable) {
|
||||
throwable = Throwables.getRootCause(throwable);
|
||||
|
||||
fetchingSkin = false;
|
||||
|
||||
if (throwable instanceof AuthenticationUnavailableException) {
|
||||
offline = true;
|
||||
} else if (throwable instanceof InvalidCredentialsException) {
|
||||
setError("hdskins.error.session");
|
||||
} else if (throwable instanceof AuthenticationException) {
|
||||
throttlingNeck = true;
|
||||
} else if (throwable instanceof HttpException) {
|
||||
HttpException ex = (HttpException)throwable;
|
||||
|
||||
int code = ex.getStatusCode();
|
||||
|
||||
if (code >= 500) {
|
||||
logger.error(ex.getReasonPhrase(), ex);
|
||||
setError("A fatal server error has ocurred (check logs for details): \n" + ex.getReasonPhrase());
|
||||
} else if (code >= 400 && code != 403 && code != 404) {
|
||||
setError(ex.getReasonPhrase());
|
||||
}
|
||||
} else {
|
||||
logger.error("Unhandled exception", throwable);
|
||||
setError(throwable.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -282,11 +292,19 @@ public class SkinUploader implements Closeable {
|
|||
retries++;
|
||||
fetchRemote();
|
||||
}
|
||||
} else if (pending) {
|
||||
fetchRemote();
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<PreviewTextureManager> loadTextures(GameProfile profile) {
|
||||
return gateway.getPreviewTextures(profile).thenApply(PreviewTextureManager::new);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return new PreviewTextureManager(gateway.getPreviewTextures(profile));
|
||||
} catch (IOException | AuthenticationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, HDSkinManager.skinDownloadExecutor); // run on main thread
|
||||
}
|
||||
|
||||
public interface ISkinUploadHandler {
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
package com.voxelmodpack.hdskins.gui;
|
||||
|
||||
/**
|
||||
* Represents the possible features that a skin server can implement.
|
||||
* Represents the possible features that a skin net can implement.
|
||||
*/
|
||||
public enum Feature {
|
||||
/**
|
||||
* Whether this skin server is usable in-game.
|
||||
*
|
||||
* Synthetic skin servers will not be queried for skins when in-game,
|
||||
* but can be still previewed, or accept textures to upload/download.
|
||||
*/
|
||||
SYNTHETIC,
|
||||
/**
|
||||
* Whether a server has write access.
|
||||
* i.e. If the server allows for users to upload a new skin.
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.io.IOException;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@Deprecated
|
||||
@ServerType("bethlehem")
|
||||
public class BethlehemSkinServer implements SkinServer {
|
||||
|
||||
|
@ -40,7 +41,7 @@ public class BethlehemSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
public void performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
SkinServer.verifyServerConnection(upload.getSession(), SERVER_ID);
|
||||
|
||||
NetClient client = new NetClient("POST", address);
|
||||
|
@ -53,10 +54,8 @@ public class BethlehemSkinServer implements SkinServer {
|
|||
|
||||
try (MoreHttpResponses response = client.send()) {
|
||||
if (!response.ok()) {
|
||||
throw new HttpException(response.getResponse());
|
||||
throw response.exception();
|
||||
}
|
||||
|
||||
return new SkinUploadResponse(response.text());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Map;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Deprecated
|
||||
@ServerType("legacy")
|
||||
public class LegacySkinServer implements SkinServer {
|
||||
|
||||
|
@ -51,21 +52,19 @@ public class LegacySkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MinecraftTexturesPayload> getPreviewTextures(GameProfile profile) {
|
||||
return CallableFutures.asyncFailableFuture(() -> {
|
||||
SkinServer.verifyServerConnection(Minecraft.getMinecraft().getSession(), SERVER_ID);
|
||||
public MinecraftTexturesPayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
SkinServer.verifyServerConnection(Minecraft.getMinecraft().getSession(), SERVER_ID);
|
||||
|
||||
if (Strings.isNullOrEmpty(gateway)) {
|
||||
throw gatewayUnsupported();
|
||||
}
|
||||
if (Strings.isNullOrEmpty(gateway)) {
|
||||
throw gatewayUnsupported();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
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 TexturesPayloadBuilder.createTexturesPayload(profile, map);
|
||||
}, HDSkinManager.skinDownloadExecutor);
|
||||
return TexturesPayloadBuilder.createTexturesPayload(profile, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -109,7 +108,7 @@ public class LegacySkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
public void performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
if (Strings.isNullOrEmpty(gateway)) {
|
||||
throw gatewayUnsupported();
|
||||
}
|
||||
|
@ -134,8 +133,6 @@ public class LegacySkinServer implements SkinServer {
|
|||
if (!response.equalsIgnoreCase("OK") && !response.endsWith("OK")) {
|
||||
throw new HttpException(response, resp.getResponseCode(), null);
|
||||
}
|
||||
|
||||
return new SkinUploadResponse(response);
|
||||
}
|
||||
|
||||
private UnsupportedOperationException gatewayUnsupported() {
|
||||
|
@ -161,11 +158,6 @@ public class LegacySkinServer implements SkinServer {
|
|||
return String.format("%s/%s/%s.png?%s", address, path, uuid, Long.toString(new Date().getTime() / 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyGateway() {
|
||||
return !Strings.isNullOrEmpty(gateway);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFeature(Feature feature) {
|
||||
switch (feature) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.voxelmodpack.hdskins.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
@ -9,16 +8,12 @@ import com.mojang.authlib.minecraft.MinecraftSessionService;
|
|||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.mumfrey.liteloader.modconfig.Exposable;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.util.CallableFutures;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface SkinServer extends Exposable {
|
||||
|
||||
|
@ -26,11 +21,6 @@ public interface SkinServer extends Exposable {
|
|||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||
.create();
|
||||
|
||||
List<SkinServer> defaultServers = Lists.newArrayList(new LegacySkinServer(
|
||||
"http://skins.voxelmodpack.com",
|
||||
"http://skinmanager.voxelmodpack.com")
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns true for any features that this skin server supports.
|
||||
*/
|
||||
|
@ -43,7 +33,7 @@ public interface SkinServer extends Exposable {
|
|||
*
|
||||
* @throws IOException If any authentication or network error occurs.
|
||||
*/
|
||||
MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException;
|
||||
MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException;
|
||||
|
||||
/**
|
||||
* Synchronously uploads a skin to this server.
|
||||
|
@ -55,36 +45,18 @@ public interface SkinServer extends Exposable {
|
|||
* @throws IOException If any authentication or network error occurs.
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException;
|
||||
|
||||
/**
|
||||
* Asynchronously uploads a skin to the server.
|
||||
*
|
||||
* Returns an incomplete future for chaining other actions to be performed after this method completes.
|
||||
* Actions are dispatched to the default skinUploadExecutor
|
||||
*
|
||||
* @param upload The payload to send.
|
||||
*/
|
||||
default CompletableFuture<SkinUploadResponse> uploadSkin(SkinUpload upload) {
|
||||
return CallableFutures.asyncFailableFuture(() -> performSkinUpload(upload), HDSkinManager.skinUploadExecutor);
|
||||
}
|
||||
void performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException;
|
||||
|
||||
/**
|
||||
* Asynchronously loads texture information for the provided profile.
|
||||
*
|
||||
* Returns an incomplete future for chaining other actions to be performed after this method completes.
|
||||
* Actions are dispatched to the default skinDownloadExecutor
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
*/
|
||||
default CompletableFuture<MinecraftTexturesPayload> getPreviewTextures(GameProfile profile) {
|
||||
return CallableFutures.asyncFailableFuture(() -> loadProfileData(profile), HDSkinManager.skinDownloadExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to validate this skin server's state.
|
||||
* Any servers with an invalid gateway format will not be loaded and generate an exception.
|
||||
*/
|
||||
default boolean verifyGateway() {
|
||||
return true;
|
||||
default MinecraftTexturesPayload getPreviewTextures(GameProfile profile) throws IOException, AuthenticationException {
|
||||
return loadProfileData(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.voxelmodpack.hdskins.server;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
|
@ -10,10 +13,36 @@ import com.google.gson.JsonSerializationContext;
|
|||
import com.google.gson.JsonSerializer;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
|
||||
public class SkinServerSerializer implements JsonSerializer<SkinServer>, JsonDeserializer<SkinServer> {
|
||||
|
||||
public static final SkinServerSerializer instance = new SkinServerSerializer();
|
||||
|
||||
private final BiMap<String, Class<? extends SkinServer>> types = HashBiMap.create(2);
|
||||
|
||||
public SkinServerSerializer() {
|
||||
// register default skin server types
|
||||
addSkinServerType(ValhallaSkinServer.class);
|
||||
addSkinServerType(YggdrasilSkinServer.class);
|
||||
addSkinServerType(LegacySkinServer.class);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
types.put(st.value(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(SkinServer src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
ServerType serverType = src.getClass().getAnnotation(ServerType.class);
|
||||
|
@ -32,6 +61,6 @@ public class SkinServerSerializer implements JsonSerializer<SkinServer>, JsonDes
|
|||
public SkinServer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
String type = json.getAsJsonObject().get("type").getAsString();
|
||||
|
||||
return context.deserialize(json, HDSkinManager.INSTANCE.getSkinServerClass(type));
|
||||
return context.deserialize(json, types.get(type));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package com.voxelmodpack.hdskins.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
|
||||
public class TexturePayload {
|
||||
|
||||
private long timestamp;
|
||||
|
||||
private UUID profileId;
|
||||
|
||||
private String profileName;
|
||||
|
||||
private boolean isPublic;
|
||||
|
||||
private Map<String, MinecraftProfileTexture> textures;
|
||||
|
||||
TexturePayload() { }
|
||||
|
||||
public TexturePayload(GameProfile profile, Map<String, MinecraftProfileTexture> textures) {
|
||||
profileId = profile.getId();
|
||||
profileName = profile.getName();
|
||||
timestamp = System.currentTimeMillis();
|
||||
|
||||
isPublic = true;
|
||||
|
||||
this.textures = new HashMap<>(textures);
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public UUID getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
public String getProfileName() {
|
||||
return profileName;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
public Map<String, MinecraftProfileTexture> getTextures() {
|
||||
return textures;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import java.util.UUID;
|
|||
|
||||
@ServerType("valhalla")
|
||||
public class ValhallaSkinServer implements SkinServer {
|
||||
private static final String API_PREFIX = "/api/v1";
|
||||
|
||||
@Expose
|
||||
private final String address;
|
||||
|
@ -38,8 +39,12 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
this.address = address;
|
||||
}
|
||||
|
||||
private String getApiPrefix() {
|
||||
return address + API_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException {
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, new HttpGet(getTexturesURI(profile)))) {
|
||||
|
||||
if (response.ok()) {
|
||||
|
@ -51,43 +56,46 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
public void performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
try {
|
||||
return uploadPlayerSkin(upload);
|
||||
uploadPlayerSkin(upload);
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().equals("Authorization failed")) {
|
||||
accessToken = null;
|
||||
return uploadPlayerSkin(upload);
|
||||
uploadPlayerSkin(upload);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadPlayerSkin(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
private void uploadPlayerSkin(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
authorize(upload.getSession());
|
||||
|
||||
switch (upload.getSchemaAction()) {
|
||||
case "none":
|
||||
return resetSkin(upload);
|
||||
resetSkin(upload);
|
||||
break;
|
||||
case "file":
|
||||
return uploadFile(upload);
|
||||
uploadFile(upload);
|
||||
break;
|
||||
case "http":
|
||||
case "https":
|
||||
return uploadUrl(upload);
|
||||
uploadUrl(upload);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unsupported URI scheme: " + upload.getSchemaAction());
|
||||
}
|
||||
}
|
||||
|
||||
private SkinUploadResponse resetSkin(SkinUpload upload) throws IOException {
|
||||
return upload(RequestBuilder.delete()
|
||||
private void resetSkin(SkinUpload upload) throws IOException {
|
||||
upload(RequestBuilder.delete()
|
||||
.setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType()))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadFile(SkinUpload upload) throws IOException {
|
||||
private void uploadFile(SkinUpload upload) throws IOException {
|
||||
final File file = new File(upload.getImage());
|
||||
|
||||
MultipartEntityBuilder b = MultipartEntityBuilder.create()
|
||||
|
@ -95,15 +103,15 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
|
||||
upload.getMetadata().forEach(b::addTextBody);
|
||||
|
||||
return upload(RequestBuilder.put()
|
||||
upload(RequestBuilder.put()
|
||||
.setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType()))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.setEntity(b.build())
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadUrl(SkinUpload upload) throws IOException {
|
||||
return upload(RequestBuilder.post()
|
||||
private void uploadUrl(SkinUpload upload) throws IOException {
|
||||
upload(RequestBuilder.post()
|
||||
.setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType()))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.addParameter("file", upload.getImage().toString())
|
||||
|
@ -111,9 +119,11 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse upload(HttpUriRequest request) throws IOException {
|
||||
private void upload(HttpUriRequest request) throws IOException {
|
||||
try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, request)) {
|
||||
return response.unwrapAsJson(SkinUploadResponse.class);
|
||||
if (!response.ok()) {
|
||||
throw response.exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +132,6 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
return;
|
||||
}
|
||||
GameProfile profile = session.getProfile();
|
||||
String token = session.getToken();
|
||||
AuthHandshake handshake = authHandshake(profile.getName());
|
||||
|
||||
if (handshake.offline) {
|
||||
|
@ -130,7 +139,7 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
}
|
||||
|
||||
// join the session server
|
||||
Minecraft.getMinecraft().getSessionService().joinServer(profile, token, handshake.serverId);
|
||||
Minecraft.getMinecraft().getSessionService().joinServer(profile, session.getToken(), handshake.serverId);
|
||||
|
||||
AuthResponse response = authResponse(profile.getName(), handshake.verifyToken);
|
||||
if (!response.userId.equals(profile.getId())) {
|
||||
|
@ -161,20 +170,20 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
private URI buildUserTextureUri(GameProfile profile, MinecraftProfileTexture.Type textureType) {
|
||||
String user = UUIDTypeAdapter.fromUUID(profile.getId());
|
||||
String skinType = textureType.name().toLowerCase(Locale.US);
|
||||
return URI.create(String.format("%s/user/%s/%s", this.address, user, skinType));
|
||||
return URI.create(String.format("%s/user/%s/%s", this.getApiPrefix(), 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())));
|
||||
return URI.create(String.format("%s/user/%s", this.getApiPrefix(), UUIDTypeAdapter.fromUUID(profile.getId())));
|
||||
}
|
||||
|
||||
private URI getHandshakeURI() {
|
||||
return URI.create(String.format("%s/auth/handshake", this.address));
|
||||
return URI.create(String.format("%s/auth/handshake", this.getApiPrefix()));
|
||||
}
|
||||
|
||||
private URI getResponseURI() {
|
||||
return URI.create(String.format("%s/auth/response", this.address));
|
||||
return URI.create(String.format("%s/auth/response", this.getApiPrefix()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
package com.voxelmodpack.hdskins.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.*;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import com.voxelmodpack.hdskins.gui.Feature;
|
||||
import com.voxelmodpack.hdskins.util.*;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Session;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
|
||||
@ServerType("mojang")
|
||||
public class YggdrasilSkinServer implements SkinServer {
|
||||
|
||||
static final SkinServer INSTANCE = new YggdrasilSkinServer();
|
||||
|
||||
private static final Set<Feature> FEATURES = Sets.newHashSet(
|
||||
Feature.SYNTHETIC,
|
||||
Feature.UPLOAD_USER_SKIN,
|
||||
Feature.DOWNLOAD_USER_SKIN,
|
||||
Feature.DELETE_USER_SKIN,
|
||||
Feature.MODEL_VARIANTS,
|
||||
Feature.MODEL_TYPES);
|
||||
|
||||
private transient final String address = "https://api.mojang.com";
|
||||
private transient final String verify = "https://authserver.mojang.com/validate";
|
||||
|
||||
private transient final boolean requireSecure = true;
|
||||
|
||||
@Override
|
||||
public boolean supportsFeature(Feature feature) {
|
||||
return FEATURES.contains(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
|
||||
|
||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = new HashMap<>();
|
||||
|
||||
Minecraft client = Minecraft.getMinecraft();
|
||||
MinecraftSessionService session = client.getSessionService();
|
||||
|
||||
profile.getProperties().clear();
|
||||
GameProfile newProfile = session.fillProfileProperties(profile, requireSecure);
|
||||
|
||||
if (newProfile == profile) {
|
||||
throw new AuthenticationException("Mojang API error occured. You may be throttled.");
|
||||
}
|
||||
profile = newProfile;
|
||||
|
||||
try {
|
||||
textures.putAll(session.getTextures(profile, requireSecure));
|
||||
} catch (InsecureTextureException e) {
|
||||
HDSkinManager.logger.error(e);
|
||||
}
|
||||
|
||||
return TexturesPayloadBuilder.createTexturesPayload(profile, textures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException {
|
||||
authorize(upload.getSession());
|
||||
|
||||
switch (upload.getSchemaAction()) {
|
||||
case "none":
|
||||
send(appendHeaders(upload, RequestBuilder.delete()));
|
||||
break;
|
||||
default:
|
||||
send(prepareUpload(upload, RequestBuilder.put()));
|
||||
}
|
||||
|
||||
Minecraft client = Minecraft.getMinecraft();
|
||||
client.getProfileProperties().clear();
|
||||
}
|
||||
|
||||
private RequestBuilder prepareUpload(SkinUpload upload, RequestBuilder request) throws IOException {
|
||||
request = appendHeaders(upload, request);
|
||||
switch (upload.getSchemaAction()) {
|
||||
case "file":
|
||||
final File file = new File(upload.getImage());
|
||||
|
||||
MultipartEntityBuilder b = MultipartEntityBuilder.create()
|
||||
.addBinaryBody("file", file, ContentType.create("image/png"), file.getName());
|
||||
|
||||
mapMetadata(upload.getMetadata()).forEach(b::addTextBody);
|
||||
|
||||
return request.setEntity(b.build());
|
||||
case "http":
|
||||
case "https":
|
||||
return request
|
||||
.addParameter("file", upload.getImage().toString())
|
||||
.addParameters(MoreHttpResponses.mapAsParameters(mapMetadata(upload.getMetadata())));
|
||||
default:
|
||||
throw new IOException("Unsupported URI scheme: " + upload.getSchemaAction());
|
||||
}
|
||||
}
|
||||
|
||||
private RequestBuilder appendHeaders(SkinUpload upload, RequestBuilder request) {
|
||||
return request
|
||||
.setUri(URI.create(String.format("%s/user/profile/%s/%s", address,
|
||||
UUIDTypeAdapter.fromUUID(upload.getSession().getProfile().getId()),
|
||||
upload.getType())))
|
||||
.addHeader("authorization", "Bearer " + upload.getSession().getToken());
|
||||
}
|
||||
|
||||
private Map<String, String> mapMetadata(Map<String, String> metadata) {
|
||||
return metadata.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
|
||||
entry -> {
|
||||
String value = entry.getValue();
|
||||
if ("model".contentEquals(entry.getKey()) && "default".contentEquals(value)) {
|
||||
return "classic";
|
||||
}
|
||||
return value;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private void authorize(Session session) throws IOException {
|
||||
RequestBuilder request = RequestBuilder.post().setUri(verify);
|
||||
request.setEntity(new TokenRequest(session).toEntity());
|
||||
|
||||
send(request);
|
||||
}
|
||||
|
||||
private void send(RequestBuilder request) throws IOException {
|
||||
try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, request.build())) {
|
||||
if (!response.ok()) {
|
||||
throw new IOException(response.json(ErrorResponse.class, "Server error wasn't in json: {}").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new IndentedToStringStyle.Builder(this)
|
||||
.append("address", address)
|
||||
.append("secured", requireSecure)
|
||||
.toString();
|
||||
}
|
||||
|
||||
static class TokenRequest {
|
||||
static final Gson GSON = new Gson();
|
||||
|
||||
@Nonnull
|
||||
private final String accessToken;
|
||||
|
||||
TokenRequest(Session session) {
|
||||
accessToken = session.getToken();
|
||||
}
|
||||
|
||||
public StringEntity toEntity() throws IOException {
|
||||
return new StringEntity(GSON.toJson(this), ContentType.APPLICATION_JSON);
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorResponse {
|
||||
String error;
|
||||
String errorMessage;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s: %s", error, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,9 @@ package com.voxelmodpack.hdskins.util;
|
|||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.*;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import com.voxelmodpack.hdskins.server.SkinServer;
|
||||
|
||||
import org.apache.http.Header;
|
||||
|
@ -11,6 +13,7 @@ import org.apache.http.HttpStatus;
|
|||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
|
||||
|
@ -20,8 +23,7 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -29,6 +31,9 @@ import java.util.stream.Stream;
|
|||
*/
|
||||
@FunctionalInterface
|
||||
public interface MoreHttpResponses extends AutoCloseable {
|
||||
Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||
.create();
|
||||
|
||||
CloseableHttpResponse getResponse();
|
||||
|
||||
|
@ -36,6 +41,10 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
return getResponseCode() == HttpStatus.SC_OK;
|
||||
}
|
||||
|
||||
default boolean json() {
|
||||
return "application/json".contentEquals(contentType().getMimeType());
|
||||
}
|
||||
|
||||
default int getResponseCode() {
|
||||
return getResponse().getStatusLine().getStatusCode();
|
||||
}
|
||||
|
@ -44,6 +53,12 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
return Optional.ofNullable(getResponse().getEntity());
|
||||
}
|
||||
|
||||
default ContentType contentType() {
|
||||
return getEntity()
|
||||
.map(ContentType::get)
|
||||
.orElse(ContentType.DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
default String getContentType() {
|
||||
return getEntity().map(HttpEntity::getContentType).map(Header::getValue).orElse("text/plain");
|
||||
}
|
||||
|
@ -86,6 +101,22 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
default <T> T json(Class<T> type, String errorMessage) throws IOException {
|
||||
return json((Type)type, errorMessage);
|
||||
}
|
||||
|
||||
default <T> T json(Type type, String errorMessage) throws IOException {
|
||||
if (!json()) {
|
||||
String text = text();
|
||||
HDSkinManager.logger.error(errorMessage, text);
|
||||
throw new IOException(text);
|
||||
}
|
||||
|
||||
try (BufferedReader reader = getReader()) {
|
||||
return GSON.fromJson(reader, type);
|
||||
}
|
||||
}
|
||||
|
||||
default <T> T unwrapAsJson(Type type) throws IOException {
|
||||
if (!"application/json".equals(getContentType())) {
|
||||
throw new IOException("Server returned a non-json response!");
|
||||
|
@ -95,7 +126,11 @@ public interface MoreHttpResponses extends AutoCloseable {
|
|||
return json(type);
|
||||
}
|
||||
|
||||
throw new IOException(json(JsonObject.class).get("message").getAsString());
|
||||
throw exception();
|
||||
}
|
||||
|
||||
default IOException exception() throws IOException {
|
||||
return new IOException(json(JsonObject.class, "Server error wasn't in json: {}").get("message").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
11
src/hdskins/resources/assets/hdskins/skins/servers.json
Normal file
11
src/hdskins/resources/assets/hdskins/skins/servers.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"overwrite": false,
|
||||
"insert": "END",
|
||||
"servers": [
|
||||
{
|
||||
"type": "valhalla",
|
||||
"address": "https://skins.minelittlepony-mod.com"
|
||||
},
|
||||
{ "type": "mojang" }
|
||||
]
|
||||
}
|
|
@ -36,10 +36,6 @@ public class MineLittlePony {
|
|||
public static final String MOD_NAME = "Mine Little Pony";
|
||||
public static final String MOD_VERSION = "@VERSION@";
|
||||
|
||||
private static final String MINELP_VALHALLA_SERVER = "http://skins.minelittlepony-mod.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");
|
||||
|
||||
private static MineLittlePony instance;
|
||||
|
@ -69,10 +65,6 @@ public class MineLittlePony {
|
|||
|
||||
MetadataSerializer ms = Minecraft.getMinecraft().getResourcePackRepository().rprMetadataSerializer;
|
||||
ms.registerMetadataSectionType(new PonyDataSerialiser(), IPonyData.class);
|
||||
|
||||
// This also makes it the default gateway server.
|
||||
SkinServer.defaultServers.add(new LegacySkinServer(MINELP_LEGACY_SERVER, MINELP_LEGACY_GATEWAY));
|
||||
SkinServer.defaultServers.add(0, new ValhallaSkinServer(MINELP_VALHALLA_SERVER));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue