mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-29 23:48:00 +01:00
Awkward code is awkward
This commit is contained in:
parent
712c98bb6b
commit
cba69ae1f5
13 changed files with 147 additions and 185 deletions
|
@ -11,14 +11,11 @@ import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
import com.mojang.authlib.properties.Property;
|
import com.mojang.authlib.properties.Property;
|
||||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||||
import com.mojang.util.UUIDTypeAdapter;
|
|
||||||
import com.mumfrey.liteloader.core.LiteLoader;
|
import com.mumfrey.liteloader.core.LiteLoader;
|
||||||
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
||||||
import com.voxelmodpack.hdskins.gui.GuiSkins;
|
import com.voxelmodpack.hdskins.gui.GuiSkins;
|
||||||
|
@ -31,7 +28,6 @@ import com.voxelmodpack.hdskins.skins.SkinServer;
|
||||||
import com.voxelmodpack.hdskins.skins.ValhallaSkinServer;
|
import com.voxelmodpack.hdskins.skins.ValhallaSkinServer;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.IImageBuffer;
|
import net.minecraft.client.renderer.IImageBuffer;
|
||||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
import net.minecraft.client.renderer.texture.TextureManager;
|
||||||
import net.minecraft.client.resources.DefaultPlayerSkin;
|
import net.minecraft.client.resources.DefaultPlayerSkin;
|
||||||
import net.minecraft.client.resources.IResourceManager;
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
|
@ -63,9 +59,6 @@ import javax.annotation.Nonnull;
|
||||||
public final class HDSkinManager implements IResourceManagerReloadListener {
|
public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
|
private static final ResourceLocation LOADING = new ResourceLocation("LOADING");
|
||||||
private static final Gson GSON = new GsonBuilder()
|
|
||||||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
|
||||||
.create();
|
|
||||||
|
|
||||||
private static final ExecutorService skinDownloadExecutor = Executors.newFixedThreadPool(8);
|
private static final ExecutorService skinDownloadExecutor = Executors.newFixedThreadPool(8);
|
||||||
public static final ListeningExecutorService skinUploadExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
|
public static final ListeningExecutorService skinUploadExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
|
||||||
|
@ -91,12 +84,14 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
private List<ISkinModifier> skinModifiers = Lists.newArrayList();
|
private List<ISkinModifier> skinModifiers = Lists.newArrayList();
|
||||||
|
|
||||||
private SkinResourceManager resources = new SkinResourceManager();
|
private SkinResourceManager resources = new SkinResourceManager();
|
||||||
// private ExecutorService executor = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
private Class<? extends GuiSkins> skinsClass = null;
|
private Class<? extends GuiSkins> skinsClass = null;
|
||||||
|
|
||||||
private HDSkinManager() {
|
public static PreviewTextureManager getPreviewTextureManager(GameProfile profile) {
|
||||||
|
return new PreviewTextureManager(INSTANCE.getGatewayServer().getPreviewTextures(profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HDSkinManager() {
|
||||||
// register default skin server types
|
// register default skin server types
|
||||||
addSkinServerType(LegacySkinServer.class);
|
addSkinServerType(LegacySkinServer.class);
|
||||||
addSkinServerType(ValhallaSkinServer.class);
|
addSkinServerType(ValhallaSkinServer.class);
|
||||||
|
@ -134,7 +129,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
Property textures = Iterables.getFirst(profile1.getProperties().get("textures"), null);
|
Property textures = Iterables.getFirst(profile1.getProperties().get("textures"), null);
|
||||||
if (textures != null) {
|
if (textures != null) {
|
||||||
String json = new String(Base64.getDecoder().decode(textures.getValue()), StandardCharsets.UTF_8);
|
String json = new String(Base64.getDecoder().decode(textures.getValue()), StandardCharsets.UTF_8);
|
||||||
MinecraftTexturesPayload texturePayload = GSON.fromJson(json, MinecraftTexturesPayload.class);
|
MinecraftTexturesPayload texturePayload = SkinServer.gson.fromJson(json, MinecraftTexturesPayload.class);
|
||||||
if (texturePayload != null) {
|
if (texturePayload != null) {
|
||||||
// name is optional
|
// name is optional
|
||||||
String name = texturePayload.getProfileName();
|
String name = texturePayload.getProfileName();
|
||||||
|
@ -176,75 +171,70 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
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) {
|
private void loadTexture(GameProfile profile, Type type, SkinAvailableCallback callback) {
|
||||||
if (profile.getId() != null) {
|
if (profile.getId() == null) {
|
||||||
Map<Type, MinecraftProfileTexture> data = getProfileData(profile);
|
return;
|
||||||
final MinecraftProfileTexture texture = data.get(type);
|
|
||||||
|
|
||||||
String skinDir = type.toString().toLowerCase() + "s/";
|
|
||||||
final ResourceLocation skin = new ResourceLocation("hdskins", skinDir + texture.getHash());
|
|
||||||
File file2 = new File(LiteLoader.getAssetsDirectory(), "hd/" + skinDir + texture.getHash().substring(0, 2) + "/" + texture.getHash());
|
|
||||||
|
|
||||||
final IImageBuffer imagebufferdownload = type == Type.SKIN ? new ImageBufferDownloadHD() : null;
|
|
||||||
|
|
||||||
ITextureObject texObject = new ThreadDownloadImageETag(file2, bustCache(texture.getUrl()),
|
|
||||||
DefaultPlayerSkin.getDefaultSkinLegacy(),
|
|
||||||
new IImageBuffer() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public BufferedImage parseUserSkin(@Nonnull BufferedImage image) {
|
|
||||||
BufferedImage image1 = image;
|
|
||||||
if (imagebufferdownload != null) {
|
|
||||||
image1 = imagebufferdownload.parseUserSkin(image);
|
|
||||||
}
|
|
||||||
return image1 == null ? image : image1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void skinAvailable() {
|
|
||||||
if (imagebufferdownload != null) {
|
|
||||||
imagebufferdownload.skinAvailable();
|
|
||||||
}
|
|
||||||
callback.skinAvailable(type, skin, texture);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// schedule texture loading on the main thread.
|
|
||||||
TextureLoader.loadTexture(skin, texObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MinecraftProfileTexture texture = getProfileData(profile).get(type);
|
||||||
|
|
||||||
|
String skinDir = type.toString().toLowerCase() + "s/";
|
||||||
|
|
||||||
|
ResourceLocation skin = new ResourceLocation("hdskins", skinDir + texture.getHash());
|
||||||
|
|
||||||
|
File etag = new File(LiteLoader.getAssetsDirectory(), "hd/" + skinDir + texture.getHash().substring(0, 2) + "/" + texture.getHash());
|
||||||
|
|
||||||
|
IImageBuffer buffer = new ImageBufferDownloadHD() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public BufferedImage parseUserSkin(@Nonnull BufferedImage image) {
|
||||||
|
if (type != Type.SKIN) {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage converted = super.parseUserSkin(image);
|
||||||
|
|
||||||
|
return converted == null ? image : converted;
|
||||||
|
}
|
||||||
|
}.withCallback(() -> callback.skinAvailable(type, skin, texture));
|
||||||
|
|
||||||
|
// schedule texture loading on the main thread.
|
||||||
|
TextureLoader.loadTexture(skin, new ThreadDownloadImageETag(etag, bustCache(texture.getUrl()), DefaultPlayerSkin.getDefaultSkinLegacy(), buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Type, MinecraftProfileTexture> loadProfileData(GameProfile profile) {
|
private Map<Type, MinecraftProfileTexture> loadProfileData(GameProfile profile) {
|
||||||
Map<Type, MinecraftProfileTexture> textures = Maps.newEnumMap(Type.class);
|
Map<Type, MinecraftProfileTexture> textures = Maps.newEnumMap(Type.class);
|
||||||
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) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
skinServers.forEach(server -> server
|
||||||
|
.loadProfileData(profile)
|
||||||
|
.map(MinecraftTexturesPayload::getTextures)
|
||||||
|
.ifPresent(a -> a.forEach(textures::putIfAbsent)));
|
||||||
|
|
||||||
return textures;
|
return textures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Type, MinecraftProfileTexture> getProfileData(GameProfile profile) {
|
public Map<Type, MinecraftProfileTexture> getProfileData(GameProfile profile) {
|
||||||
boolean was = !skins.asMap().containsKey(profile);
|
boolean was = !skins.asMap().containsKey(profile);
|
||||||
|
|
||||||
Map<Type, MinecraftProfileTexture> textures = skins.getUnchecked(profile);
|
Map<Type, MinecraftProfileTexture> textures = skins.getUnchecked(profile);
|
||||||
|
|
||||||
// This is the initial value. Refreshing will load it asynchronously.
|
// This is the initial value. Refreshing will load it asynchronously.
|
||||||
if (was) {
|
if (was) {
|
||||||
skins.refresh(profile);
|
skins.refresh(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return textures;
|
return textures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSkinServerType(Class<? extends SkinServer> type) {
|
public void addSkinServerType(Class<? extends SkinServer> type) {
|
||||||
Preconditions.checkArgument(!type.isInterface(), "type cannot be an interface");
|
Preconditions.checkArgument(!type.isInterface(), "type cannot be an interface");
|
||||||
Preconditions.checkArgument(!Modifier.isAbstract(type.getModifiers()), "type cannot be abstract");
|
Preconditions.checkArgument(!Modifier.isAbstract(type.getModifiers()), "type cannot be abstract");
|
||||||
|
|
||||||
ServerType st = type.getAnnotation(ServerType.class);
|
ServerType st = type.getAnnotation(ServerType.class);
|
||||||
if (st == null) {
|
|
||||||
throw new IllegalArgumentException("class is not annotated with @ServerType");
|
Preconditions.checkArgument(st != null, "class is not annotated with @ServerType");
|
||||||
}
|
|
||||||
this.skinServerTypes.put(st.value(), type);
|
skinServerTypes.put(st.value(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<? extends SkinServer> getSkinServerClass(String type) {
|
public Class<? extends SkinServer> getSkinServerClass(String type) {
|
||||||
|
@ -264,15 +254,11 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PreviewTextureManager getPreviewTextureManager(GameProfile profile) {
|
|
||||||
return new PreviewTextureManager(INSTANCE.getGatewayServer().getPreviewTextures(profile));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addClearListener(ISkinCacheClearListener listener) {
|
public void addClearListener(ISkinCacheClearListener listener) {
|
||||||
clearListeners.add(listener);
|
clearListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearSkinCache() {
|
public void clearSkinCache() {
|
||||||
LiteLoaderLogger.info("Clearing local player skin cache");
|
LiteLoaderLogger.info("Clearing local player skin cache");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -281,20 +267,17 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
TextureManager textures = Minecraft.getMinecraft().getTextureManager();
|
TextureManager textures = Minecraft.getMinecraft().getTextureManager();
|
||||||
|
|
||||||
INSTANCE.skinCache.values().stream().flatMap(m -> m.values().stream())
|
skinCache.values().stream().flatMap(m -> m.values().stream()).forEach(textures::deleteTexture);
|
||||||
.forEach(textures::deleteTexture);
|
skinCache.clear();
|
||||||
INSTANCE.skinCache.clear();
|
skins.invalidateAll();
|
||||||
INSTANCE.skins.invalidateAll();
|
|
||||||
} catch (IOException var1) {
|
} catch (IOException var1) {
|
||||||
var1.printStackTrace();
|
var1.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE.clearListeners = INSTANCE.clearListeners.stream()
|
clearListeners = clearListeners.stream().filter(this::onSkinCacheCleared).collect(Collectors.toList());
|
||||||
.filter(HDSkinManager::onSkinCacheCleared)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean onSkinCacheCleared(ISkinCacheClearListener callback) {
|
private boolean onSkinCacheCleared(ISkinCacheClearListener callback) {
|
||||||
try {
|
try {
|
||||||
return callback.onSkinCacheCleared();
|
return callback.onSkinCacheCleared();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -313,6 +296,12 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
||||||
return loc == null ? res : loc;
|
return loc == null ? res : loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void convertSkin(BufferedImage image) {
|
||||||
|
Graphics graphics = image.getGraphics();
|
||||||
|
convertSkin(image, graphics);
|
||||||
|
graphics.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void convertSkin(BufferedImage image, Graphics dest) {
|
public void convertSkin(BufferedImage image, Graphics dest) {
|
||||||
skinModifiers.forEach(a -> a.convertSkin(image, dest));
|
skinModifiers.forEach(a -> a.convertSkin(image, dest));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.voxelmodpack.hdskins;
|
||||||
import net.minecraft.client.renderer.IImageBuffer;
|
import net.minecraft.client.renderer.IImageBuffer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
@ -12,6 +13,13 @@ public class ImageBufferDownloadHD implements IImageBuffer {
|
||||||
private Graphics graphics;
|
private Graphics graphics;
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
|
|
||||||
|
private Runnable callback;
|
||||||
|
|
||||||
|
public ImageBufferDownloadHD withCallback(Runnable callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings({"SuspiciousNameCombination", "NullableProblems"})
|
@SuppressWarnings({"SuspiciousNameCombination", "NullableProblems"})
|
||||||
|
@ -19,6 +27,7 @@ public class ImageBufferDownloadHD implements IImageBuffer {
|
||||||
if (downloadedImage == null) {
|
if (downloadedImage == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int imageWidth = downloadedImage.getWidth();
|
int imageWidth = downloadedImage.getWidth();
|
||||||
int imageHeight = downloadedImage.getHeight();
|
int imageHeight = downloadedImage.getHeight();
|
||||||
if (imageHeight == imageWidth) {
|
if (imageHeight == imageWidth) {
|
||||||
|
@ -61,5 +70,8 @@ public class ImageBufferDownloadHD implements IImageBuffer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void skinAvailable() {
|
public void skinAvailable() {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class LiteModHDSkins implements InitCompleteListener, ViewportListener, C
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void upgradeSettings(String version, File configPath, File oldConfigPath) {
|
public void upgradeSettings(String version, File configPath, File oldConfigPath) {
|
||||||
HDSkinManager.clearSkinCache();
|
HDSkinManager.INSTANCE.clearSkinCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class PreviewTexture extends ThreadDownloadImageData {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTextureUploaded() {
|
public boolean isTextureUploaded() {
|
||||||
return uploaded && this.getGlTextureId() > -1;
|
return uploaded && getGlTextureId() > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,10 +6,9 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.IImageBuffer;
|
import net.minecraft.client.renderer.IImageBuffer;
|
||||||
import net.minecraft.client.resources.SkinManager;
|
import net.minecraft.client.resources.SkinManager.SkinAvailableCallback;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -26,37 +25,21 @@ public class PreviewTextureManager {
|
||||||
this.textures = textures;
|
this.textures = textures;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IImageBuffer getPreviewImageBuffer(MinecraftProfileTexture texture, ResourceLocation location, Type type, SkinManager.SkinAvailableCallback callback) {
|
|
||||||
if (type != Type.SKIN) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
IImageBuffer buffer = new ImageBufferDownloadHD();
|
|
||||||
|
|
||||||
return 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public PreviewTexture getPreviewTexture(ResourceLocation location, Type type, ResourceLocation def, @Nullable SkinManager.SkinAvailableCallback callback) {
|
public PreviewTexture getPreviewTexture(ResourceLocation location, Type type, ResourceLocation def, @Nullable SkinAvailableCallback callback) {
|
||||||
if (!textures.containsKey(type)) {
|
if (!textures.containsKey(type)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftProfileTexture texture = textures.get(type);
|
MinecraftProfileTexture texture = textures.get(type);
|
||||||
PreviewTexture skinTexture = new PreviewTexture(texture, def, getPreviewImageBuffer(texture, location, type, callback));
|
|
||||||
|
IImageBuffer buffer = type != Type.SKIN ? null : new ImageBufferDownloadHD().withCallback(() -> {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.skinAvailable(type, location, new MinecraftProfileTexture(texture.getUrl(), Maps.newHashMap()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PreviewTexture skinTexture = new PreviewTexture(texture, def, buffer);
|
||||||
|
|
||||||
Minecraft.getMinecraft().getTextureManager().loadTexture(location, skinTexture);
|
Minecraft.getMinecraft().getTextureManager().loadTexture(location, skinTexture);
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
|
||||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||||
import com.voxelmodpack.hdskins.skins.SkinServer;
|
|
||||||
import com.voxelmodpack.hdskins.skins.SkinUploadResponse;
|
import com.voxelmodpack.hdskins.skins.SkinUploadResponse;
|
||||||
import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG;
|
import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.Gui;
|
import net.minecraft.client.gui.Gui;
|
||||||
import net.minecraft.client.gui.GuiButton;
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
@ -93,20 +93,23 @@ public class GuiSkins extends GuiScreen {
|
||||||
private boolean thinArmType = false;
|
private boolean thinArmType = false;
|
||||||
|
|
||||||
public GuiSkins() {
|
public GuiSkins() {
|
||||||
|
instance = this;
|
||||||
|
|
||||||
Minecraft minecraft = Minecraft.getMinecraft();
|
Minecraft minecraft = Minecraft.getMinecraft();
|
||||||
|
|
||||||
GameProfile profile = minecraft.getSession().getProfile();
|
GameProfile profile = minecraft.getSession().getProfile();
|
||||||
|
|
||||||
localPlayer = getModel(profile);
|
localPlayer = getModel(profile);
|
||||||
remotePlayer = getModel(profile);
|
remotePlayer = getModel(profile);
|
||||||
RenderManager rm = Minecraft.getMinecraft().getRenderManager();
|
|
||||||
|
RenderManager rm = minecraft.getRenderManager();
|
||||||
rm.renderEngine = minecraft.getTextureManager();
|
rm.renderEngine = minecraft.getTextureManager();
|
||||||
rm.options = minecraft.gameSettings;
|
rm.options = minecraft.gameSettings;
|
||||||
rm.renderViewEntity = localPlayer;
|
rm.renderViewEntity = localPlayer;
|
||||||
reloadRemoteSkin();
|
|
||||||
fetchingSkin = true;
|
|
||||||
|
|
||||||
instance = this;
|
reloadRemoteSkin();
|
||||||
|
|
||||||
|
fetchingSkin = true;
|
||||||
|
|
||||||
panorama = new CubeMap(this);
|
panorama = new CubeMap(this);
|
||||||
initPanorama();
|
initPanorama();
|
||||||
|
@ -126,6 +129,7 @@ public class GuiSkins extends GuiScreen {
|
||||||
if (!(Keyboard.isKeyDown(Keyboard.KEY_LEFT) || Keyboard.isKeyDown(Keyboard.KEY_RIGHT))) {
|
if (!(Keyboard.isKeyDown(Keyboard.KEY_LEFT) || Keyboard.isKeyDown(Keyboard.KEY_RIGHT))) {
|
||||||
updateCounter++;
|
updateCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
panorama.update();
|
panorama.update();
|
||||||
|
|
||||||
localPlayer.updateModel();
|
localPlayer.updateModel();
|
||||||
|
@ -186,7 +190,9 @@ public class GuiSkins extends GuiScreen {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initGui() {
|
public void initGui() {
|
||||||
enableDnd();
|
GLWindow.current().setDropTargetListener((FileDropListener) files -> {
|
||||||
|
files.stream().findFirst().ifPresent(instance::loadLocalFile);
|
||||||
|
});
|
||||||
|
|
||||||
panorama.init();
|
panorama.init();
|
||||||
|
|
||||||
|
@ -215,18 +221,12 @@ public class GuiSkins extends GuiScreen {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableDnd() {
|
|
||||||
GLWindow.current().setDropTargetListener((FileDropListener) files -> {
|
|
||||||
files.stream().findFirst().ifPresent(instance::loadLocalFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGuiClosed() {
|
public void onGuiClosed() {
|
||||||
super.onGuiClosed();
|
super.onGuiClosed();
|
||||||
localPlayer.releaseTextures();
|
localPlayer.releaseTextures();
|
||||||
remotePlayer.releaseTextures();
|
remotePlayer.releaseTextures();
|
||||||
HDSkinManager.clearSkinCache();
|
HDSkinManager.INSTANCE.clearSkinCache();
|
||||||
|
|
||||||
GLWindow.current().clearDropTargetListener();
|
GLWindow.current().clearDropTargetListener();
|
||||||
}
|
}
|
||||||
|
@ -438,8 +438,7 @@ public class GuiSkins extends GuiScreen {
|
||||||
drawHoveringText(I18n.format(text), mouseX, y);
|
drawHoveringText(I18n.format(text), mouseX, y);
|
||||||
}
|
}
|
||||||
if (btnAbout.isMouseOver()) {
|
if (btnAbout.isMouseOver()) {
|
||||||
SkinServer gateway = HDSkinManager.INSTANCE.getGatewayServer();
|
drawHoveringText(Splitter.on("\r\n").splitToList(HDSkinManager.INSTANCE.getGatewayServer().toString()), mouseX, mouseY);
|
||||||
drawHoveringText(Splitter.on("\r\n").splitToList(gateway.toString()), mouseX, mouseY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fetchingSkin) {
|
if (fetchingSkin) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class HDSkinsConfigPanel implements ConfigPanel {
|
||||||
Minecraft mc = Minecraft.getMinecraft();
|
Minecraft mc = Minecraft.getMinecraft();
|
||||||
|
|
||||||
if (button.mousePressed(mc, mouseX, mouseY)) {
|
if (button.mousePressed(mc, mouseX, mouseY)) {
|
||||||
HDSkinManager.clearSkinCache();
|
HDSkinManager.INSTANCE.clearSkinCache();
|
||||||
} else if (checkbox.mousePressed(mc, mouseX, mouseY)) {
|
} else if (checkbox.mousePressed(mc, mouseX, mouseY)) {
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
mod.experimentalSkinDrop = checkbox.checked;
|
mod.experimentalSkinDrop = checkbox.checked;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
@Mixin(ImageBufferDownload.class)
|
@Mixin(ImageBufferDownload.class)
|
||||||
|
@ -20,11 +19,7 @@ public abstract class MixinImageBufferDownload implements IImageBuffer {
|
||||||
private void update(BufferedImage image, CallbackInfoReturnable<BufferedImage> ci) {
|
private void update(BufferedImage image, CallbackInfoReturnable<BufferedImage> ci) {
|
||||||
// convert skins from mojang server
|
// convert skins from mojang server
|
||||||
if (image.getHeight() == 32) {
|
if (image.getHeight() == 32) {
|
||||||
BufferedImage image2 = ci.getReturnValue();
|
HDSkinManager.INSTANCE.convertSkin(ci.getReturnValue());
|
||||||
Graphics graphics = image2.getGraphics();
|
|
||||||
HDSkinManager.INSTANCE.convertSkin(image2, graphics);
|
|
||||||
graphics.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Mixin(NetworkPlayerInfo.class)
|
@Mixin(NetworkPlayerInfo.class)
|
||||||
public abstract class MixinPlayerInfo {
|
public abstract class MixinPlayerInfo {
|
||||||
|
|
||||||
|
@ -42,8 +40,7 @@ public abstract class MixinPlayerInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getTextureLocation(CallbackInfoReturnable<ResourceLocation> ci, Type type) {
|
private void getTextureLocation(CallbackInfoReturnable<ResourceLocation> ci, Type type) {
|
||||||
Optional<ResourceLocation> texture = HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), type, true);
|
HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), type, true).ifPresent(ci::setReturnValue);
|
||||||
texture.ifPresent(ci::setReturnValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "getSkinType",
|
@Inject(method = "getSkinType",
|
||||||
|
@ -52,13 +49,11 @@ public abstract class MixinPlayerInfo {
|
||||||
private void getSkinType(CallbackInfoReturnable<String> ci) {
|
private void getSkinType(CallbackInfoReturnable<String> ci) {
|
||||||
MinecraftProfileTexture skin = HDSkinManager.INSTANCE.getProfileData(getGameProfile()).get(Type.SKIN);
|
MinecraftProfileTexture skin = HDSkinManager.INSTANCE.getProfileData(getGameProfile()).get(Type.SKIN);
|
||||||
if (skin != null) {
|
if (skin != null) {
|
||||||
String type = skin.getMetadata("model");
|
HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), Type.SKIN, false).ifPresent(res -> {
|
||||||
if (type == null)
|
String type = skin.getMetadata("model");
|
||||||
type = "default";
|
|
||||||
String type1 = type;
|
|
||||||
Optional<ResourceLocation> texture = HDSkinManager.INSTANCE.getSkinLocation(getGameProfile(), Type.SKIN, false);
|
|
||||||
|
|
||||||
texture.ifPresent((res) -> ci.setReturnValue(type1));
|
ci.setReturnValue(type == null ? "default" : type);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.voxelmodpack.hdskins.mixin;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.tileentity.TileEntitySkullRenderer;
|
import net.minecraft.client.renderer.tileentity.TileEntitySkullRenderer;
|
||||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||||
import net.minecraft.tileentity.TileEntitySkull;
|
import net.minecraft.tileentity.TileEntitySkull;
|
||||||
|
@ -13,27 +14,20 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Mixin(TileEntitySkullRenderer.class)
|
@Mixin(TileEntitySkullRenderer.class)
|
||||||
public abstract class MixinSkullRenderer extends TileEntitySpecialRenderer<TileEntitySkull> {
|
public abstract class MixinSkullRenderer extends TileEntitySpecialRenderer<TileEntitySkull> {
|
||||||
|
|
||||||
@Redirect(
|
@Redirect(method = "renderSkull",
|
||||||
method = "renderSkull",
|
at = @At(value = "INVOKE",
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/client/renderer/tileentity/TileEntitySkullRenderer;bindTexture(Lnet/minecraft/util/ResourceLocation;)V",
|
target = "Lnet/minecraft/client/renderer/tileentity/TileEntitySkullRenderer;bindTexture(Lnet/minecraft/util/ResourceLocation;)V",
|
||||||
ordinal = 4))
|
ordinal = 4))
|
||||||
private void onBindTexture(TileEntitySkullRenderer tesr, ResourceLocation rl, float x, float y, float z, EnumFacing facing, float rotation, int meta,
|
private void onBindTexture(TileEntitySkullRenderer tesr, ResourceLocation rl, float x, float y, float z, // ow my back
|
||||||
@Nullable GameProfile profile, int p_180543_8_, float ticks) {
|
EnumFacing facing, float rotation, int meta, @Nullable GameProfile profile, int a, float ticks) {
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
Optional<ResourceLocation> skin = HDSkinManager.INSTANCE.getSkinLocation(profile, Type.SKIN, true);
|
bindTexture(HDSkinManager.INSTANCE.getSkinLocation(profile, Type.SKIN, true).orElse(rl));
|
||||||
if (skin.isPresent())
|
} else {
|
||||||
// rebind
|
|
||||||
bindTexture(skin.get());
|
|
||||||
else
|
|
||||||
bindTexture(rl);
|
|
||||||
} else
|
|
||||||
bindTexture(rl);
|
bindTexture(rl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class ImageLoader implements Supplier<ResourceLocation> {
|
||||||
private final ResourceLocation original;
|
private final ResourceLocation original;
|
||||||
|
|
||||||
public ImageLoader(ResourceLocation loc) {
|
public ImageLoader(ResourceLocation loc) {
|
||||||
this.original = loc;
|
original = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,7 +49,6 @@ public class ImageLoader implements Supplier<ResourceLocation> {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static BufferedImage getImage(ResourceLocation res) {
|
private static BufferedImage getImage(ResourceLocation res) {
|
||||||
|
|
||||||
try (InputStream in = mc.getResourceManager().getResource(res).getInputStream()) {
|
try (InputStream in = mc.getResourceManager().getResource(res).getInputStream()) {
|
||||||
return TextureUtil.readBufferedImage(in);
|
return TextureUtil.readBufferedImage(in);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -59,9 +58,9 @@ public class ImageLoader implements Supplier<ResourceLocation> {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private ResourceLocation loadSkin(BufferedImage image) {
|
private ResourceLocation loadSkin(BufferedImage image) {
|
||||||
|
|
||||||
ResourceLocation conv = new ResourceLocation(original.getResourceDomain() + "-converted", original.getResourcePath());
|
ResourceLocation conv = new ResourceLocation(original.getResourceDomain() + "-converted", original.getResourcePath());
|
||||||
boolean success = mc.getTextureManager().loadTexture(conv, new DynamicTextureImage(image));
|
boolean success = mc.getTextureManager().loadTexture(conv, new DynamicTextureImage(image));
|
||||||
|
|
||||||
return success ? conv : null;
|
return success ? conv : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -25,8 +26,9 @@ import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
public class SkinResourceManager implements IResourceManagerReloadListener {
|
public class SkinResourceManager implements IResourceManagerReloadListener {
|
||||||
|
private final Gson GSON = new Gson();
|
||||||
|
|
||||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
private ExecutorService executor;
|
||||||
|
|
||||||
private Map<UUID, Skin> uuidSkins = Maps.newHashMap();
|
private Map<UUID, Skin> uuidSkins = Maps.newHashMap();
|
||||||
private Map<String, Skin> namedSkins = Maps.newHashMap();
|
private Map<String, Skin> namedSkins = Maps.newHashMap();
|
||||||
|
@ -41,15 +43,16 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
|
||||||
executor = Executors.newSingleThreadExecutor();
|
executor = Executors.newSingleThreadExecutor();
|
||||||
inProgress.clear();
|
inProgress.clear();
|
||||||
converted.clear();
|
converted.clear();
|
||||||
|
|
||||||
for (String domain : resourceManager.getResourceDomains()) {
|
for (String domain : resourceManager.getResourceDomains()) {
|
||||||
try {
|
try {
|
||||||
for (IResource res : resourceManager.getAllResources(new ResourceLocation(domain, "textures/skins/skins.json"))) {
|
for (IResource res : resourceManager.getAllResources(new ResourceLocation(domain, "textures/skins/skins.json"))) {
|
||||||
try {
|
try {
|
||||||
SkinData data = getSkinData(res.getInputStream());
|
for (Skin s : getSkinData(res.getInputStream())) {
|
||||||
for (Skin s : data.skins) {
|
|
||||||
if (s.uuid != null) {
|
if (s.uuid != null) {
|
||||||
uuidSkins.put(s.uuid, s);
|
uuidSkins.put(s.uuid, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.name != null) {
|
if (s.name != null) {
|
||||||
namedSkins.put(s.name, s);
|
namedSkins.put(s.name, s);
|
||||||
}
|
}
|
||||||
|
@ -58,16 +61,13 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
|
||||||
LiteLoaderLogger.warning(je, "Invalid skins.json in %s", res.getResourcePackName());
|
LiteLoaderLogger.warning(je, "Invalid skins.json in %s", res.getResourcePackName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException ignored) { }
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SkinData getSkinData(InputStream stream) {
|
private List<Skin> getSkinData(InputStream stream) {
|
||||||
try {
|
try {
|
||||||
return new Gson().fromJson(new InputStreamReader(stream), SkinData.class);
|
return GSON.fromJson(new InputStreamReader(stream), SkinData.class).skins;
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(stream);
|
IOUtils.closeQuietly(stream);
|
||||||
}
|
}
|
||||||
|
@ -75,16 +75,14 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ResourceLocation getPlayerTexture(GameProfile profile, Type type) {
|
public ResourceLocation getPlayerTexture(GameProfile profile, Type type) {
|
||||||
if (type != Type.SKIN)
|
if (type == Type.SKIN) {
|
||||||
// not supported
|
Skin skin = getSkin(profile);
|
||||||
return null;
|
if (skin != null) {
|
||||||
|
return getConvertedResource(skin.getTexture());
|
||||||
Skin skin = getSkin(profile);
|
}
|
||||||
if (skin != null) {
|
|
||||||
final ResourceLocation res = skin.getTexture();
|
|
||||||
return getConvertedResource(res);
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return null; // not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,31 +97,29 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
|
||||||
return converted.get(res);
|
return converted.get(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSkinResource(@Nullable final ResourceLocation res) {
|
/**
|
||||||
|
* read and convert in a new thread
|
||||||
|
*/
|
||||||
|
private void loadSkinResource(@Nullable ResourceLocation res) {
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
// read and convert in a new thread
|
inProgress.computeIfAbsent(res, r -> CompletableFuture.supplyAsync(new ImageLoader(r), executor).whenComplete((loc, t) -> {
|
||||||
this.inProgress.computeIfAbsent(res, r -> CompletableFuture.supplyAsync(new ImageLoader(r), executor)
|
if (loc != null) {
|
||||||
.whenComplete((loc, t) -> {
|
converted.put(res, loc);
|
||||||
if (loc != null)
|
} else {
|
||||||
converted.put(res, loc);
|
LogManager.getLogger().warn("Errored while processing {}. Using original.", res, t);
|
||||||
else {
|
converted.put(res, res);
|
||||||
LogManager.getLogger().warn("Errored while processing {}. Using original.", res, t);
|
}
|
||||||
converted.put(res, res);
|
}));
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Skin getSkin(GameProfile profile) {
|
private Skin getSkin(GameProfile profile) {
|
||||||
Skin skin = this.uuidSkins.get(profile.getId());
|
Skin skin = uuidSkins.get(profile.getId());
|
||||||
if (skin == null) {
|
if (skin != null) {
|
||||||
skin = this.namedSkins.get(profile.getName());
|
return skin;
|
||||||
}
|
}
|
||||||
return skin;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return namedSkins.get(profile.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface SkinServer extends Exposable {
|
public interface SkinServer extends Exposable {
|
||||||
|
|
||||||
static final Gson gson = new GsonBuilder()
|
public static final Gson gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue