Merge branch 'skins_fix'

This commit is contained in:
Sollace 2018-06-07 20:31:51 +02:00
commit 9611198a86
12 changed files with 343 additions and 55 deletions

View file

@ -38,6 +38,7 @@ import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -45,6 +46,8 @@ import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -62,6 +65,8 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
private boolean enabled = true;
private List<ISkinCacheClearListener> clearListeners = Lists.newArrayList();
private List<SkinServer> skinServers = Lists.newArrayList();
private Map<UUID, Map<Type, ResourceLocation>> skinCache = Maps.newHashMap();
@ -123,6 +128,10 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
return skin == LOADING ? Optional.empty() : Optional.of(skin);
}
private String bustCache(String url) {
return url + (url.indexOf('?') > -1 ? '&' : '?') + Long.toString(new Date().getTime()/1000);
}
private void loadTexture(GameProfile profile, final Type type, final SkinAvailableCallback callback) {
if (profile.getId() != null) {
Map<Type, MinecraftProfileTexture> data = getProfileData(profile);
@ -134,7 +143,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
final IImageBuffer imagebufferdownload = type == Type.SKIN ? new ImageBufferDownloadHD() : null;
ITextureObject texObject = new ThreadDownloadImageETag(file2, texture.getUrl(),
ITextureObject texObject = new ThreadDownloadImageETag(file2, bustCache(texture.getUrl()),
DefaultPlayerSkin.getDefaultSkinLegacy(),
new IImageBuffer() {
@Nonnull
@ -223,11 +232,16 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
}
public void addClearListener(ISkinCacheClearListener listener) {
clearListeners.add(listener);
}
public static void clearSkinCache() {
LiteLoaderLogger.info("Clearing local player skin cache");
try {
FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "skins"));
FileUtils.deleteDirectory(new File(LiteLoader.getAssetsDirectory(), "hd"));
TextureManager textures = Minecraft.getMinecraft().getTextureManager();
INSTANCE.skinCache.values().stream()
.flatMap(m -> m.values().stream())
@ -238,6 +252,19 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
var1.printStackTrace();
}
INSTANCE.clearListeners = INSTANCE.clearListeners.stream()
.filter(HDSkinManager::onSkinCacheCleared)
.collect(Collectors.toList());
}
private static boolean onSkinCacheCleared(ISkinCacheClearListener callback) {
try {
return callback.onSkinCacheCleared();
} catch (Exception e) {
LiteLoaderLogger.warning("Exception ancountered calling skin listener '{}'. It will be removed.", callback.getClass().getName());
e.printStackTrace();
return false;
}
}
public void addSkinModifier(ISkinModifier modifier) {

View file

@ -0,0 +1,6 @@
package com.voxelmodpack.hdskins;
@FunctionalInterface
public interface ISkinCacheClearListener {
boolean onSkinCacheCleared();
}

View file

@ -0,0 +1,29 @@
package com.voxelmodpack.hdskins;
public final class Later extends Thread {
private final int delay;
protected Later(int delay, Runnable runnable) {
super(runnable);
this.delay = delay;
setDaemon(true);
setName("Later#" + getId());
}
public static void performLater(int delay, Runnable callable) {
new Later(delay, callable).start();
}
public static void performNow(Runnable callable) {
new Later(0, callable).start();
}
@Override
public void run() {
try {
if (delay > 0) sleep(delay);
} catch (InterruptedException e) {}
super.run();
}
}

View file

@ -0,0 +1,184 @@
package com.voxelmodpack.hdskins.gui;
import java.awt.Canvas;
import java.awt.Frame;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.io.Closeable;
import java.io.IOException;
import java.util.TooManyListenersException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import com.google.common.collect.Lists;
import com.voxelmodpack.hdskins.Later;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
public class GLWindow implements Closeable {
static GLWindow instance = null;
public static GLWindow current() {
if (instance == null) {
instance = new GLWindow();
}
return instance;
}
private final DropTarget dt;
private final JFrame frame;
private DropTargetListener saved = null;
private final int frameX = 15;
private final int frameY = 36;
private final Minecraft mc = Minecraft.getMinecraft();
private int state = 0;
private GLWindow() {
int x = Display.getX();
int y = Display.getY();
int w = Display.getWidth() + frameX;
int h = Display.getHeight() + frameY;
Canvas canvas = new Canvas();
frame = new JFrame(Display.getTitle());
frame.setResizable(Display.isResizable());
frame.setLocation(x, y);
frame.setSize(w, h);
frame.getContentPane().setLayout(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent windowEvent) {
Minecraft.getMinecraft().shutdown();
}
});
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent componentEvent) {
canvas.setBounds(0, 0, frame.getWidth() - frameX, frame.getHeight() - frameY);
}
});
frame.addWindowStateListener(new WindowStateListener() {
@Override
public void windowStateChanged(WindowEvent event) {
state = event.getNewState();
Later.performLater(1, () -> {
canvas.setBounds(0, 0, frame.getWidth() - frameX, frame.getHeight() - frameY);
});
}
});
setIcons(frame);
frame.getContentPane().add(canvas);
frame.setVisible(true);
try {
Display.setParent(canvas);
} catch (LWJGLException e) {
e.printStackTrace();
}
if (Display.getWidth() == Display.getDesktopDisplayMode().getWidth()) {
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
}
state = frame.getExtendedState();
if (mc.isFullScreen()) {
try {
Display.setFullscreen(true);
} catch (LWJGLException e) {
e.printStackTrace();
}
}
dt = new DropTarget();
canvas.setDropTarget(dt);
}
private final void setIcons(JFrame frame) {
try {
frame.setIconImages(Lists.newArrayList(
ImageIO.read(mc.getResourceManager().getResource(new ResourceLocation("icons/icon_16x16.png")).getInputStream()),
ImageIO.read(mc.getResourceManager().getResource(new ResourceLocation("icons/icon_32x32.png")).getInputStream())
));
} catch (IOException e) {
e.printStackTrace();
}
}
public void setDropTargetListener(DropTargetListener dtl) {
if (saved != null) {
dt.removeDropTargetListener(saved);
}
if (dtl != null) {
try {
dt.addDropTargetListener(dtl);
} catch (TooManyListenersException e) { }
saved = dtl;
}
}
public static void dispose() {
if (instance != null) {
try {
instance.close();
} catch (IOException ignored) {
}
}
}
@Override
public void close() throws IOException {
mc.addScheduledTask(() -> {
try {
Display.setParent(null);
} catch (LWJGLException e) {
e.printStackTrace();
}
try {
if (mc.isFullScreen()) {
Display.setFullscreen(true);
} else {
if ((state & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) {
Display.setLocation(0, 0);
Display.setDisplayMode(Display.getDesktopDisplayMode());
} else {
Display.setDisplayMode(new DisplayMode(mc.displayWidth, mc.displayHeight));
Display.setLocation(frame.getX(), frame.getY());
}
Display.setResizable(false);
Display.setResizable(true);
}
} catch (LWJGLException e) {
e.printStackTrace();
}
frame.setVisible(false);
frame.dispose();
instance = null;
});
}
}

View file

@ -10,6 +10,7 @@ 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.Later;
import com.voxelmodpack.hdskins.skins.SkinUploadResponse;
import com.voxelmodpack.hdskins.upload.awt.ThreadOpenFilePNG;
import net.minecraft.client.Minecraft;
@ -37,14 +38,12 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import java.awt.Color;
import java.awt.Window.Type;
import java.awt.dnd.DropTarget;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.DoubleBuffer;
import java.nio.file.Path;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.swing.*;
@ -88,7 +87,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
private File selectedSkin;
private float uploadOpacity = 0.0F;
private float lastPartialTick;
private JFrame fileDrop;
private static GuiSkins instance;
private MinecraftProfileTexture.Type textureType = SKIN;
@ -104,6 +104,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
rm.renderViewEntity = this.localPlayer;
this.reloadRemoteSkin();
this.fetchingSkin = true;
instance = this;
}
protected EntityPlayerModel getModel(GameProfile profile) {
@ -171,6 +173,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
@Override
public void initGui() {
enableDnd();
this.initPanoramaRenderer();
this.buttonList.clear();
this.buttonList.add(this.btnBrowse = new GuiButton(0, 30, this.height - 36, 60, 20, "Browse..."));
@ -189,35 +192,9 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
}
private void enableDnd() {
if (fileDrop != null) {
fileDrop.setVisible(true);
return;
}
fileDrop = new JFrame("Skin Drop");
fileDrop.setType(Type.UTILITY);
fileDrop.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
fileDrop.setResizable(false);
fileDrop.setTitle("Skin Drop");
fileDrop.setSize(256, 256);
// fileDrop.setAlwaysOnTop(true);
fileDrop.getContentPane().setLayout(null);
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.GRAY));
panel.setBounds(10, 11, 230, 205);
fileDrop.getContentPane().add(panel);
JLabel txtInst = new JLabel("Drop skin file here");
txtInst.setHorizontalAlignment(SwingConstants.CENTER);
txtInst.setVerticalAlignment(SwingConstants.CENTER);
panel.add(txtInst);
DropTarget dt = new DropTarget();
fileDrop.setDropTarget(dt);
try {
dt.addDropTargetListener((FileDropListener) files -> files.stream().findFirst().ifPresent(this::loadLocalFile));
fileDrop.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
GLWindow.current().setDropTargetListener((FileDropListener) files -> {
files.stream().findFirst().ifPresent(instance::loadLocalFile);
});
}
private void initPanoramaRenderer() {
@ -227,10 +204,9 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
@Override
public void onGuiClosed() {
super.onGuiClosed();
if (this.fileDrop != null)
this.fileDrop.dispose();
this.localPlayer.releaseTextures();
this.remotePlayer.releaseTextures();
localPlayer.releaseTextures();
remotePlayer.releaseTextures();
HDSkinManager.clearSkinCache();
}
private void onFileOpenDialogClosed(JFileChooser fileDialog, int dialogResult) {

View file

@ -0,0 +1,29 @@
package com.voxelmodpack.hdskins.mixin;
import java.io.IOException;
import org.lwjgl.LWJGLException;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.voxelmodpack.hdskins.gui.GLWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.crash.CrashReport;
@Mixin(Minecraft.class)
public class MixinMinecraft {
//public void displayCrashReport(CrashReport crashReportIn)
@Inject(method = "displayCrashReport(Lnet/minecraft/crash/CrashReport;)V", at = @At("HEAD"))
private void onGameCrash(CrashReport report, CallbackInfo info) {
GLWindow.dispose();
}
@Inject(method = "init()V", at = @At("RETURN"))
private void onInit(CallbackInfo info) throws LWJGLException, IOException {
GLWindow.current();
}
}

View file

@ -2,9 +2,17 @@ package com.voxelmodpack.hdskins.upload.awt;
import net.minecraft.client.Minecraft;
import java.awt.AWTEvent;
import java.awt.event.MouseEvent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileFilter;
import org.lwjgl.opengl.Display;
import com.voxelmodpack.hdskins.Later;
/**
* Base class for "open file" dialog threads
*
@ -19,6 +27,10 @@ public abstract class ThreadOpenFile extends Thread {
*/
protected final IOpenFileCallback parentScreen;
private JFileChooser fileDialog;
private JFrame parent = null;
protected ThreadOpenFile(Minecraft minecraft, String dialogTitle, IOpenFileCallback callback)
throws IllegalStateException {
if (minecraft.isFullScreen()) {
@ -29,14 +41,35 @@ public abstract class ThreadOpenFile extends Thread {
this.dialogTitle = dialogTitle;
}
@Override
public void start() {
Later.performLater(0, this);
}
public ThreadOpenFile setParent(JFrame parent) {
this.parent = parent;
this.parent.setAlwaysOnTop(true);
return this;
}
@Override
public void run() {
JFileChooser fileDialog = new JFileChooser();
if (parent == null) {
parent = new JFrame("InternalDialog");
parent.setAlwaysOnTop(true);
}
parent.requestFocusInWindow();
parent.setVisible(false);
fileDialog = new JFileChooser();
fileDialog.setDialogTitle(this.dialogTitle);
fileDialog.setFileFilter(this.getFileFilter());
int dialogResult = fileDialog.showOpenDialog(null);
int dialogResult = fileDialog.showOpenDialog(parent);
parent.setVisible(true);
this.parentScreen.onFileOpenDialogClosed(fileDialog, dialogResult);
}

View file

@ -4,6 +4,7 @@
"package": "com.voxelmodpack.hdskins.mixin",
"refmap": "hdskins.mixin.refmap.json",
"mixins": [
"MixinMinecraft",
"MixinGuiMainMenu",
"MixinImageBufferDownload",
"MixinPlayerInfo",

View file

@ -27,8 +27,7 @@ public class MineLittlePony {
public static final String MOD_NAME = "Mine Little Pony";
public static final String MOD_VERSION = "@VERSION@";
private static final String SKIN_SERVER_URL = "minelpskins.voxelmodpack.com";
private static final String GATEWAY_URL = "minelpskinmanager.voxelmodpack.com";
private static final String MINELP_LEGACY_SERVER = "legacy:http://minelpskins.voxelmodpack.com;http://minelpskinmanager.voxelmodpack.com";
private static final KeyBinding SETTINGS_GUI = new KeyBinding("Settings", Keyboard.KEY_F9, "Mine Little Pony");
@ -46,6 +45,7 @@ public class MineLittlePony {
config = new PonyConfig();
ponyManager = new PonyManager(config);
renderManager = new PonyRenderManager();
LiteLoader.getInstance().registerExposable(config, null);
@ -57,7 +57,7 @@ public class MineLittlePony {
ms.registerMetadataSectionType(new PonyDataSerialzier(), IPonyData.class);
// This also makes it the default gateway server.
SkinServer.defaultServers.add("legacy:" + SKIN_SERVER_URL + ";" + GATEWAY_URL);
SkinServer.defaultServers.add(MINELP_LEGACY_SERVER);
}
/**
@ -70,6 +70,7 @@ public class MineLittlePony {
// manager.setGatewayURL(GATEWAY_URL);
manager.addSkinModifier(new PonySkinModifier());
// logger.info("Set MineLP skin server URL.");
manager.addClearListener(ponyManager);
RenderManager rm = minecraft.getRenderManager();
renderManager.initialisePlayerRenderers(rm);

View file

@ -7,6 +7,8 @@ import com.google.gson.JsonParseException;
import com.minelittlepony.model.PMAPI;
import com.minelittlepony.pony.data.Pony;
import com.minelittlepony.pony.data.PonyLevel;
import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.ISkinCacheClearListener;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
@ -29,7 +31,7 @@ import java.util.stream.Collectors;
* The PonyManager is responsible for reading and recoding all the pony data associated with an entity of skin.
*
*/
public class PonyManager implements IResourceManagerReloadListener {
public class PonyManager implements IResourceManagerReloadListener, ISkinCacheClearListener {
public static final ResourceLocation STEVE = new ResourceLocation("minelittlepony", "textures/entity/steve_pony.png");
public static final ResourceLocation ALEX = new ResourceLocation("minelittlepony", "textures/entity/alex_pony.png");
@ -45,7 +47,6 @@ public class PonyManager implements IResourceManagerReloadListener {
private PonyConfig config;
private Map<ResourceLocation, Pony> poniesCache = Maps.newHashMap();
private Map<ResourceLocation, Pony> backgroudPoniesCache = Maps.newHashMap();
public PonyManager(PonyConfig config) {
this.config = config;
@ -83,6 +84,9 @@ public class PonyManager implements IResourceManagerReloadListener {
}
public Pony getPony(NetworkPlayerInfo playerInfo) {
// force load HDSkins if they're not available
HDSkinManager.INSTANCE.getProfileData(playerInfo.getGameProfile());
ResourceLocation skin = playerInfo.getLocationSkin();
UUID uuid = playerInfo.getGameProfile().getId();
@ -149,7 +153,6 @@ public class PonyManager implements IResourceManagerReloadListener {
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
poniesCache.clear();
backgroudPoniesCache.clear();
backgroundPonyList.clear();
try {
for (IResource res : resourceManager.getAllResources(BGPONIES_JSON)) {
@ -202,4 +205,11 @@ public class PonyManager implements IResourceManagerReloadListener {
return ponies.stream().map(this::apply).collect(Collectors.toList());
}
}
@Override
public boolean onSkinCacheCleared() {
MineLittlePony.logger.info("Flushed {} cached ponies.", poniesCache.size());
poniesCache.clear();
return true;
}
}

View file

@ -40,12 +40,4 @@ public class GuiSkinsMineLP extends GuiSkins {
ponyManager.removePony(resource);
}
}
@Override
public void onGuiClosed() {
super.onGuiClosed();
ponyManager.removePony(localPlayer.getSkinTexture());
ponyManager.removePony(remotePlayer.getSkinTexture());
}
}

View file

@ -81,7 +81,7 @@ public class PonyTail extends PlaneRenderer implements IModelPart {
@Override
public void renderPart(float scale) {
this.render(scale);
render(scale);
}
private class TailSegment extends PlaneRenderer {