diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java index ac9358ac..965aba4d 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java @@ -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 clearListeners = Lists.newArrayList(); + private List skinServers = Lists.newArrayList(); private Map> 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 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) { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/ISkinCacheClearListener.java b/src/hdskins/java/com/voxelmodpack/hdskins/ISkinCacheClearListener.java new file mode 100644 index 00000000..d1a21d0f --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/ISkinCacheClearListener.java @@ -0,0 +1,6 @@ +package com.voxelmodpack.hdskins; + +@FunctionalInterface +public interface ISkinCacheClearListener { + boolean onSkinCacheCleared(); +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/Later.java b/src/hdskins/java/com/voxelmodpack/hdskins/Later.java new file mode 100644 index 00000000..2e979663 --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/Later.java @@ -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(); + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GLWindow.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GLWindow.java new file mode 100644 index 00000000..25059878 --- /dev/null +++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GLWindow.java @@ -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; + }); + } +} diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java index a1f232dc..9ab9853a 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java @@ -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 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 poniesCache = Maps.newHashMap(); - private Map 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; + } } diff --git a/src/main/java/com/minelittlepony/hdskins/gui/GuiSkinsMineLP.java b/src/main/java/com/minelittlepony/hdskins/gui/GuiSkinsMineLP.java index 1a540e88..87b434c2 100644 --- a/src/main/java/com/minelittlepony/hdskins/gui/GuiSkinsMineLP.java +++ b/src/main/java/com/minelittlepony/hdskins/gui/GuiSkinsMineLP.java @@ -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()); - - } } diff --git a/src/main/java/com/minelittlepony/model/components/PonyTail.java b/src/main/java/com/minelittlepony/model/components/PonyTail.java index ee58f1ed..4409fc0e 100644 --- a/src/main/java/com/minelittlepony/model/components/PonyTail.java +++ b/src/main/java/com/minelittlepony/model/components/PonyTail.java @@ -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 {