From 8c2cf9011e0cc2684405f0b5802c76488d592ae3 Mon Sep 17 00:00:00 2001 From: Marius Metzger Date: Sun, 25 Jan 2015 20:36:02 +0100 Subject: [PATCH] Cleaned up even more GUIs Added possibility to create Thumbnails and show them in the Replay Manager --- .../eu/crushedpixel/replaymod/ReplayMod.java | 11 - .../replaymod/api/client/ApiClient.java | 45 ++-- .../replaymod/api/client/SimpleApiClient.java | 3 +- .../replaymod/entities/CameraEntity.java | 2 +- .../replaymod/events/GuiEventHandler.java | 73 ++++-- .../replaymod/events/GuiReplayOverlay.java | 61 ++++- .../replaymod/events/ReplayTickHandler.java | 33 --- .../replaymod/gui/GuiExitReplay.java | 66 ----- .../replaymod/gui/GuiReplaySaving.java | 13 +- .../{ => replaymanager}/GuiRenameReplay.java | 2 +- .../GuiReplayListEntry.java | 39 ++- .../GuiReplayListExtended.java | 10 +- .../{ => replaymanager}/GuiReplayManager.java | 87 +++++-- .../gui/replaymanager/ResourceHelper.java | 24 ++ .../recording/ConnectionEventHandler.java | 2 - .../replaymod/recording/DataListener.java | 7 +- .../replaymod/replay/ReplayHandler.java | 7 + .../replaymod/replay/ReplaySender.java | 4 + .../screenshot/ReplayScreenshot.java | 234 ++++++++++++++++++ .../replaymod/settings/ReplaySettings.java | 16 +- .../replaymod/utils/ImageUtils.java | 69 ++++++ .../{api/client => utils}/StreamTools.java | 2 +- .../replaymod/utils/ZipFileUtils.java | 59 +++++ 23 files changed, 663 insertions(+), 206 deletions(-) delete mode 100644 src/main/java/eu/crushedpixel/replaymod/events/ReplayTickHandler.java delete mode 100644 src/main/java/eu/crushedpixel/replaymod/gui/GuiExitReplay.java rename src/main/java/eu/crushedpixel/replaymod/gui/{ => replaymanager}/GuiRenameReplay.java (98%) rename src/main/java/eu/crushedpixel/replaymod/gui/{ => replaymanager}/GuiReplayListEntry.java (69%) rename src/main/java/eu/crushedpixel/replaymod/gui/{ => replaymanager}/GuiReplayListExtended.java (96%) rename src/main/java/eu/crushedpixel/replaymod/gui/{ => replaymanager}/GuiReplayManager.java (79%) create mode 100644 src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/ResourceHelper.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/screenshot/ReplayScreenshot.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/utils/ImageUtils.java rename src/main/java/eu/crushedpixel/replaymod/{api/client => utils}/StreamTools.java (96%) create mode 100644 src/main/java/eu/crushedpixel/replaymod/utils/ZipFileUtils.java diff --git a/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java b/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java index 8ada0e25..67b585d7 100644 --- a/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java +++ b/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java @@ -1,8 +1,5 @@ package eu.crushedpixel.replaymod; -import java.io.IOException; - -import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; @@ -10,18 +7,13 @@ import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.Mod.Instance; -import net.minecraftforge.fml.common.SidedProxy; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import eu.crushedpixel.replaymod.api.client.ApiClient; -import eu.crushedpixel.replaymod.api.client.ApiException; -import eu.crushedpixel.replaymod.api.client.holders.AuthKey; import eu.crushedpixel.replaymod.authentication.AuthenticationHandler; import eu.crushedpixel.replaymod.events.GuiEventHandler; import eu.crushedpixel.replaymod.events.GuiReplayOverlay; import eu.crushedpixel.replaymod.events.RecordingHandler; -import eu.crushedpixel.replaymod.events.ReplayTickHandler; import eu.crushedpixel.replaymod.recording.ConnectionEventHandler; import eu.crushedpixel.replaymod.settings.ReplaySettings; @@ -78,9 +70,6 @@ public class ReplayMod public void init(FMLInitializationEvent event) { FMLCommonHandler.instance().bus().register(new ConnectionEventHandler()); MinecraftForge.EVENT_BUS.register(new GuiEventHandler()); - ReplayTickHandler tickHandler = new ReplayTickHandler(); - FMLCommonHandler.instance().bus().register(tickHandler); - MinecraftForge.EVENT_BUS.register(tickHandler); recordingHandler = new RecordingHandler(); FMLCommonHandler.instance().bus().register(recordingHandler); diff --git a/src/main/java/eu/crushedpixel/replaymod/api/client/ApiClient.java b/src/main/java/eu/crushedpixel/replaymod/api/client/ApiClient.java index 479eb836..99494d66 100644 --- a/src/main/java/eu/crushedpixel/replaymod/api/client/ApiClient.java +++ b/src/main/java/eu/crushedpixel/replaymod/api/client/ApiClient.java @@ -3,7 +3,9 @@ package eu.crushedpixel.replaymod.api.client; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.URL; +import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.List; @@ -26,6 +28,7 @@ import eu.crushedpixel.replaymod.api.client.holders.Category; import eu.crushedpixel.replaymod.api.client.holders.FileInfo; import eu.crushedpixel.replaymod.api.client.holders.Success; import eu.crushedpixel.replaymod.api.client.holders.UserFiles; +import eu.crushedpixel.replaymod.utils.StreamTools; public class ApiClient { @@ -61,23 +64,25 @@ public class ApiClient { builder.put("auth", auth); builder.put("category", category.getId()); String url = builder.toString(); - + HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(url); - - ((InputStream)client).close(); - + FileEntity entity = new FileEntity(file); post.setEntity(entity); HttpResponse response = client.execute(post); - - JsonElement element = jsonParser.parse(EntityUtils.toString(response.getEntity())); - try { - ApiError err = gson.fromJson(element, ApiError.class); - if(err.getDesc() != null) { - throw new ApiException(err); - } - } catch(Exception e) {} + + //((InputStream)client).close(); TODO: Find working solution + + if(response.getStatusLine().getStatusCode() != 200) { + JsonElement element = jsonParser.parse(EntityUtils.toString(response.getEntity())); + try { + ApiError err = gson.fromJson(element, ApiError.class); + if(err.getDesc() != null) { + throw new ApiException(err); + } + } catch(Exception e) {} + } } public void downloadFile(String auth, int file, File target) throws IOException { @@ -86,11 +91,19 @@ public class ApiClient { builder.put("id", file); String url = builder.toString(); URL website = new URL(url); - InputStream is = website.openStream(); - try { //If valid json, an error occured - jsonParser.parse(StreamTools.readStreamtoString(is)); - } catch(Exception e) { + HttpURLConnection con = (HttpURLConnection)website.openConnection(); + InputStream is = con.getInputStream(); + + if(con.getResponseCode() == 200) { Files.copy(is, target.toPath(), StandardCopyOption.REPLACE_EXISTING); + } else { + JsonElement element = jsonParser.parse(StreamTools.readStreamtoString(is)); + try { + ApiError err = gson.fromJson(element, ApiError.class); + if(err.getDesc() != null) { + throw new ApiException(err); + } + } catch(Exception e) {} } } diff --git a/src/main/java/eu/crushedpixel/replaymod/api/client/SimpleApiClient.java b/src/main/java/eu/crushedpixel/replaymod/api/client/SimpleApiClient.java index dbe98e63..54c4b7dd 100644 --- a/src/main/java/eu/crushedpixel/replaymod/api/client/SimpleApiClient.java +++ b/src/main/java/eu/crushedpixel/replaymod/api/client/SimpleApiClient.java @@ -11,6 +11,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import eu.crushedpixel.replaymod.api.client.holders.ApiError; +import eu.crushedpixel.replaymod.utils.StreamTools; public class SimpleApiClient { @@ -73,7 +74,7 @@ public class SimpleApiClient { HttpURLConnection.setFollowRedirects(false); try { URL url = new URL(urlString); - httpUrlConnection = (HttpURLConnection) url.openConnection(); + httpUrlConnection = (HttpURLConnection)url.openConnection(); httpUrlConnection.setRequestMethod("GET"); diff --git a/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java b/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java index e52d1fcd..ebff3543 100644 --- a/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java +++ b/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java @@ -28,7 +28,7 @@ public class CameraEntity extends EntityPlayer { private double decay = 6; //decays by 75% per second; private long lastCall = 0; - + //frac = time since last tick public void updateMovement() { Minecraft mc = Minecraft.getMinecraft(); diff --git a/src/main/java/eu/crushedpixel/replaymod/events/GuiEventHandler.java b/src/main/java/eu/crushedpixel/replaymod/events/GuiEventHandler.java index fe2c525f..42f55072 100644 --- a/src/main/java/eu/crushedpixel/replaymod/events/GuiEventHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/GuiEventHandler.java @@ -1,5 +1,6 @@ package eu.crushedpixel.replaymod.events; +import java.util.ArrayList; import java.util.List; import net.minecraft.client.Minecraft; @@ -9,38 +10,49 @@ import net.minecraft.client.gui.GuiDisconnected; import net.minecraft.client.gui.GuiIngameMenu; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiOptions; +import net.minecraft.client.gui.GuiVideoSettings; import net.minecraft.client.gui.inventory.GuiInventory; import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.GameSettings.Options; import net.minecraftforge.client.event.GuiOpenEvent; import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; import net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.authentication.AuthenticationHandler; -import eu.crushedpixel.replaymod.gui.GuiExitReplay; -import eu.crushedpixel.replaymod.gui.GuiReplayManager; import eu.crushedpixel.replaymod.gui.GuiReplaySaving; import eu.crushedpixel.replaymod.gui.GuiReplaySettings; -import eu.crushedpixel.replaymod.recording.ConnectionEventHandler; +import eu.crushedpixel.replaymod.gui.replaymanager.GuiReplayManager; +import eu.crushedpixel.replaymod.gui.replaymanager.ResourceHelper; import eu.crushedpixel.replaymod.replay.ReplayHandler; public class GuiEventHandler { + private static Minecraft mc = Minecraft.getMinecraft(); + + private static List allowedGUIs = new ArrayList() { + { + add(GuiReplaySettings.class); + add(GuiReplaySaving.class); + add(GuiIngameMenu.class); + add(GuiOptions.class); + add(GuiVideoSettings.class); + } + }; + @SubscribeEvent public void onGui(GuiOpenEvent event) { if(!AuthenticationHandler.isAuthenticated()) return; + if(event.gui != null && GuiReplaySaving.replaySaving && !allowedGUIs.contains(event.gui.getClass())) { + event.gui = new GuiReplaySaving(event.gui); + return; + } + if(!(event.gui instanceof GuiReplayManager)) ResourceHelper.freeResources(); if(event.gui instanceof GuiChat || event.gui instanceof GuiInventory) { if(ReplayHandler.replayActive()) { event.setCanceled(true); } } - else if(event.gui instanceof GuiIngameMenu) { - if(ReplayHandler.replayActive()) { - event.gui = new GuiExitReplay(); - } - } - else if(event.gui instanceof GuiDisconnected) { if(!ReplayHandler.replayActive() && System.currentTimeMillis() - ReplayHandler.lastExit < 5000) { event.setCanceled(true); @@ -53,7 +65,18 @@ public class GuiEventHandler { @SubscribeEvent public void onInit(InitGuiEvent event) { - if(event.gui instanceof GuiMainMenu) { + if(event.gui instanceof GuiIngameMenu && ReplayHandler.replayActive()) { + for(GuiButton b : new ArrayList(event.buttonList)) { + if(b.id == 1) { + b.displayString = "Exit Replay"; + b.yPosition -= 24*2; + } else if(b.id >= 5 && b.id <= 7) { + event.buttonList.remove(b); + } else if(b.id != 4) { + b.yPosition -= 24*2; + } + } + } else if(event.gui instanceof GuiMainMenu) { int i1 = event.gui.height / 4 + 48; for(GuiButton b : (List)event.buttonList) { @@ -69,18 +92,32 @@ public class GuiEventHandler { event.buttonList.add(new GuiButton(9001, event.gui.width / 2 - 155, event.gui.height / 6 + 48 - 6 - 24, 310, 20, "Replay Mod Settings...")); } } - + @SubscribeEvent public void onButton(ActionPerformedEvent event) { if(!AuthenticationHandler.isAuthenticated()) return; if(event.gui instanceof GuiMainMenu && event.button.id == REPLAY_MANAGER_ID) { - if(ConnectionEventHandler.saving) { - Minecraft.getMinecraft().displayGuiScreen(new GuiReplaySaving()); - } else { - Minecraft.getMinecraft().displayGuiScreen(new GuiReplayManager()); - } + mc.displayGuiScreen(new GuiReplayManager()); } else if(event.gui instanceof GuiOptions && event.button.id == REPLAY_OPTIONS_ID) { - Minecraft.getMinecraft().displayGuiScreen(new GuiReplaySettings(event.gui)); + mc.displayGuiScreen(new GuiReplaySettings(event.gui)); + } + + if(ReplayHandler.isReplaying() && event.gui instanceof GuiIngameMenu && event.button.id == 1) { + event.button.enabled = false; + Thread t = new Thread(new Runnable() { + @Override + public void run() { + mc.displayGuiScreen(new GuiReplaySaving(new GuiMainMenu())); + ReplayHandler.setSpeed(1f); + ReplayHandler.endReplay(); + + mc.gameSettings.setOptionFloatValue(Options.GAMMA, ReplayHandler.getInitialGamma()); + + ReplayHandler.lastExit = System.currentTimeMillis(); + mc.theWorld.sendQuittingDisconnectingPacket(); + } + }); + t.run(); } } diff --git a/src/main/java/eu/crushedpixel/replaymod/events/GuiReplayOverlay.java b/src/main/java/eu/crushedpixel/replaymod/events/GuiReplayOverlay.java index 4bf4156f..5c8a87af 100644 --- a/src/main/java/eu/crushedpixel/replaymod/events/GuiReplayOverlay.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/GuiReplayOverlay.java @@ -19,6 +19,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.GuiIngameForge; import net.minecraftforge.client.event.MouseEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.fml.client.FMLClientHandler; @@ -43,6 +44,7 @@ import eu.crushedpixel.replaymod.holders.TimeKeyframe; import eu.crushedpixel.replaymod.reflection.MCPNames; import eu.crushedpixel.replaymod.replay.ReplayHandler; import eu.crushedpixel.replaymod.replay.ReplayProcess; +import eu.crushedpixel.replaymod.screenshot.ReplayScreenshot; public class GuiReplayOverlay extends Gui { @@ -77,6 +79,8 @@ public class GuiReplayOverlay extends Gui { private boolean mouseDown = false; + private boolean requestScreenshot = false; + private Field drawBlockOutline; public GuiReplayOverlay() { @@ -114,6 +118,10 @@ public class GuiReplayOverlay extends Gui { if(mc != null && mc.thePlayer != null) MinecraftTicker.runMouseKeyboardTick(mc); } + if(requestScreenshot) { + requestScreenshot = false; + ReplayScreenshot.saveScreenshot(mc.getFramebuffer()); + } } public void resetUI() throws Exception { @@ -128,10 +136,42 @@ public class GuiReplayOverlay extends Gui { public void onRenderGui(RenderGameOverlayEvent.Post event) throws IllegalArgumentException, IllegalAccessException { if(!ReplayHandler.replayActive()) { GuiIngameForge.renderExperiance = true; + GuiIngameForge.renderArmor = true; + GuiIngameForge.renderAir = true; + GuiIngameForge.renderHealth = true; + GuiIngameForge.renderHotbar = true; + GuiIngameForge.renderFood = true; + GuiIngameForge.renderBossHealth = true; + GuiIngameForge.renderCrosshairs = true; + GuiIngameForge.renderHelmet = true; + GuiIngameForge.renderPortal = true; + GuiIngameForge.renderHealthMount = true; + GuiIngameForge.renderJumpBar = true; + GuiIngameForge.renderObjective = true; + return; + } + + if(event.type == ElementType.PLAYER_LIST) { + if(event.isCancelable()) { + event.setCanceled(true); + } return; } GuiIngameForge.renderExperiance = false; + GuiIngameForge.renderArmor = false; + GuiIngameForge.renderAir = false; + GuiIngameForge.renderHealth = false; + GuiIngameForge.renderHotbar = false; + GuiIngameForge.renderFood = false; + GuiIngameForge.renderBossHealth = false; + GuiIngameForge.renderCrosshairs = false; + GuiIngameForge.renderHelmet = false; + GuiIngameForge.renderPortal = false; + GuiIngameForge.renderHealthMount = false; + GuiIngameForge.renderJumpBar = false; + GuiIngameForge.renderObjective = false; + GL11.glEnable(GL11.GL_BLEND); drawBlockOutline.set(Minecraft.getMinecraft().entityRenderer, false); @@ -204,11 +244,11 @@ public class GuiReplayOverlay extends Gui { CameraEntity cam = ReplayHandler.getCameraEntity(); if(cam != null) { - ReplayHandler.setLastPosition(new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, cam.rotationYaw)); + ReplayHandler.setLastPosition(new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, cam.rotationYaw)); } else { ReplayHandler.setLastPosition(null); } - + ReplayHandler.setReplayPos((int)time); } } @@ -316,7 +356,7 @@ public class GuiReplayOverlay extends Gui { try { speedSlider.drawButton(mc, mouseX, mouseY); } catch(Exception e) {} - + GlStateManager.resetColor(); Entity player = ReplayHandler.getCameraEntity(); @@ -746,7 +786,7 @@ public class GuiReplayOverlay extends Gui { return false; } } - + @SubscribeEvent public void onMouseMove(MouseEvent event) { if(!ReplayHandler.replayActive()) return; @@ -783,15 +823,16 @@ public class GuiReplayOverlay extends Gui { @SubscribeEvent public void onKeyInput(KeyInputEvent event) { + if(!ReplayHandler.replayActive()) return; if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) { return; } - - if(Keyboard.isKeyDown(Keyboard.KEY_V) && !Keyboard.isRepeatEvent() && Keyboard.getKeyCount() == 1) { + + if(Keyboard.isKeyDown(Keyboard.KEY_V) && !Keyboard.isRepeatEvent()) { ReplayMod.replaySettings.setLightingEnabled(!ReplayMod.replaySettings.isLightingEnabled()); } - + KeyBinding[] keyBindings = Minecraft.getMinecraft().gameSettings.keyBindings; for(KeyBinding kb : keyBindings) { if(!kb.isKeyDown()) { @@ -833,6 +874,12 @@ public class GuiReplayOverlay extends Gui { mc.displayGuiScreen(new GuiMouseInput()); continue; } + + //TODO: Register key handlers + if(kb.getKeyDescription().equals("key.screenshot") && kb.isPressed()) { + ReplayScreenshot.prepareScreenshot(); + requestScreenshot = true; + } } catch(Exception e) { e.printStackTrace(); } diff --git a/src/main/java/eu/crushedpixel/replaymod/events/ReplayTickHandler.java b/src/main/java/eu/crushedpixel/replaymod/events/ReplayTickHandler.java deleted file mode 100644 index 8d6c8168..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/events/ReplayTickHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package eu.crushedpixel.replaymod.events; - -import java.lang.reflect.Field; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.entity.Entity; -import net.minecraft.world.WorldSettings.GameType; -import net.minecraftforge.client.event.FOVUpdateEvent; -import net.minecraftforge.client.event.RenderWorldEvent; -import net.minecraftforge.event.entity.player.AchievementEvent; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.fml.common.eventhandler.Event; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent; -import eu.crushedpixel.replaymod.reflection.MCPNames; -import eu.crushedpixel.replaymod.replay.ReplayHandler; - -public class ReplayTickHandler { - - private Minecraft mc = Minecraft.getMinecraft(); - - @SubscribeEvent - public void onPlayer(PlayerEvent event) { - if(ReplayHandler.replayActive() && event.isCancelable()) { - event.setCanceled(true); - } - } - -} diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiExitReplay.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiExitReplay.java deleted file mode 100644 index 44cd89dd..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiExitReplay.java +++ /dev/null @@ -1,66 +0,0 @@ -package eu.crushedpixel.replaymod.gui; - -import java.io.IOException; - -import eu.crushedpixel.replaymod.replay.ReplayHandler; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiIngameMenu; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.GameSettings.Options; - -public class GuiExitReplay extends GuiIngameMenu { - - @Override - public void initGui() - { - this.buttonList.clear(); - byte b0 = -16; - boolean flag = true; - this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + b0, I18n.format("menu.returnToMenu", new Object[0]))); - - if (!this.mc.isIntegratedServerRunning()) - { - ((GuiButton)this.buttonList.get(0)).displayString = I18n.format("Exit Replay", new Object[0]); - } - - this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 4 + 24 + b0, I18n.format("menu.returnToGame", new Object[0]))); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + b0, 98, 20, I18n.format("menu.options", new Object[0]))); - this.buttonList.add(new GuiButton(12, this.width / 2 + 2, this.height / 4 + 96 + b0, 98, 20, I18n.format("fml.menu.modoptions"))); - GuiButton guibutton; - this.buttonList.add(guibutton = new GuiButton(7, this.width / 2 - 100, this.height / 4 + 72 + b0, 200, 20, I18n.format("menu.shareToLan", new Object[0]))); - this.buttonList.add(new GuiButton(5, this.width / 2 - 100, this.height / 4 + 48 + b0, 98, 20, I18n.format("gui.achievements", new Object[0]))); - this.buttonList.add(new GuiButton(6, this.width / 2 + 2, this.height / 4 + 48 + b0, 98, 20, I18n.format("gui.stats", new Object[0]))); - guibutton.enabled = this.mc.isSingleplayer() && !this.mc.getIntegratedServer().getPublic(); - } - - @Override - protected void actionPerformed(GuiButton button) throws IOException - { - if(button.id == 1) { - button.enabled = false; - - Thread t = new Thread(new Runnable() { - - @Override - public void run() { - mc.displayGuiScreen(new GuiReplaySaving()); - ReplayHandler.endReplay(); - ReplayHandler.setSpeed(1f); - - mc.gameSettings.setOptionFloatValue(Options.GAMMA, ReplayHandler.getInitialGamma()); - - ReplayHandler.lastExit = System.currentTimeMillis(); - mc.theWorld.sendQuittingDisconnectingPacket(); - } - }); - - t.run(); - - } else { - super.actionPerformed(button); - } - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySaving.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySaving.java index c0320b3b..c651e59a 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySaving.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySaving.java @@ -2,6 +2,7 @@ package eu.crushedpixel.replaymod.gui; import java.awt.Color; +import eu.crushedpixel.replaymod.gui.replaymanager.GuiReplayManager; import eu.crushedpixel.replaymod.recording.ConnectionEventHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiIngameMenu; @@ -10,14 +11,22 @@ import net.minecraftforge.fml.client.FMLClientHandler; public class GuiReplaySaving extends GuiScreen { + public static boolean replaySaving = false; + + private GuiScreen waiting = null; + + public GuiReplaySaving(GuiScreen waiting) { + this.waiting = waiting; + } + @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); this.drawCenteredString(this.fontRendererObj, "Saving Replay File...", this.width / 2, 20, 16777215); this.drawCenteredString(this.fontRendererObj, "Please wait while your recent Replay is being saved.", this.width / 2, 40, 16777215); super.drawScreen(mouseX, mouseY, partialTicks); - if(!ConnectionEventHandler.saving) { - Minecraft.getMinecraft().displayGuiScreen(new GuiReplayManager()); + if(!replaySaving) { + Minecraft.getMinecraft().displayGuiScreen(waiting); } } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiRenameReplay.java b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiRenameReplay.java similarity index 98% rename from src/main/java/eu/crushedpixel/replaymod/gui/GuiRenameReplay.java rename to src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiRenameReplay.java index df8707ce..50d52f13 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiRenameReplay.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiRenameReplay.java @@ -1,4 +1,4 @@ -package eu.crushedpixel.replaymod.gui; +package eu.crushedpixel.replaymod.gui.replaymanager; import java.io.File; import java.io.IOException; diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListEntry.java b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListEntry.java similarity index 69% rename from src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListEntry.java rename to src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListEntry.java index b218bd57..6827538d 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListEntry.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListEntry.java @@ -1,11 +1,15 @@ -package eu.crushedpixel.replaymod.gui; +package eu.crushedpixel.replaymod.gui.replaymanager; +import java.awt.image.BufferedImage; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.zip.ZipInputStream; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; @@ -13,6 +17,8 @@ import net.minecraft.client.gui.GuiListExtended.IGuiListEntry; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.util.ResourceLocation; import eu.crushedpixel.replaymod.recording.ReplayMetaData; public class GuiReplayListEntry implements IGuiListEntry { @@ -22,8 +28,11 @@ public class GuiReplayListEntry implements IGuiListEntry { private ReplayMetaData metaData; private String fileName; - - + + private final ResourceLocation textureResource; + private DynamicTexture dynTex = null; + + private BufferedImage image = null; public ReplayMetaData getMetaData() { return metaData; @@ -41,20 +50,34 @@ public class GuiReplayListEntry implements IGuiListEntry { this.fileName = fileName; } - private boolean selected = false; private GuiReplayListExtended parent; - public GuiReplayListEntry(GuiReplayListExtended parent, String fileName, ReplayMetaData metaData) { + public GuiReplayListEntry(GuiReplayListExtended parent, String fileName, ReplayMetaData metaData, BufferedImage image) { this.metaData = metaData; this.fileName = fileName; this.parent = parent; + this.textureResource = new ResourceLocation("thumbs/"+fileName); + dynTex = null; + this.image = image; } @Override - public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected) - { + public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected) { minecraft.fontRendererObj.drawString(fileName, x + 3, y + 1, 16777215); + //Draw thumbnail + if(image != null) { + if(dynTex == null) { + dynTex = new DynamicTexture(image); + minecraft.getTextureManager().loadTexture(textureResource, dynTex); + dynTex.updateDynamicTexture(); + ResourceHelper.registerResource(textureResource); + } + + minecraft.getTextureManager().bindTexture(textureResource); //Will be freed by the ResourceHelper + Gui.drawScaledCustomSizeModalRect(x-60, y, 0, 0, 1280, 720, 57, 32, 1280, 720); + } + List list = new ArrayList(); list.add(metaData.getServerName()+" ("+dateFormat.format(new Date(metaData.getDate()))+")"); @@ -67,6 +90,8 @@ public class GuiReplayListEntry implements IGuiListEntry { for (int l1 = 0; l1 < Math.min(list.size(), 2); ++l1) { minecraft.fontRendererObj.drawString((String)list.get(l1), x + 3, y + 12 + minecraft.fontRendererObj.FONT_HEIGHT * l1, 8421504); } + + } @Override diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListExtended.java b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListExtended.java similarity index 96% rename from src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListExtended.java rename to src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListExtended.java index a2ef2a9e..d642f6c6 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayListExtended.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayListExtended.java @@ -1,5 +1,6 @@ -package eu.crushedpixel.replaymod.gui; +package eu.crushedpixel.replaymod.gui.replaymanager; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; @@ -48,6 +49,7 @@ public class GuiReplayListExtended extends GuiListExtended { for (int j1 = 0; j1 < i1; ++j1) { + int k1 = p_148120_2_ + j1 * this.slotHeight + this.headerPadding; int l1 = this.slotHeight - 4; @@ -76,7 +78,7 @@ public class GuiReplayListExtended extends GuiListExtended { tessellator.draw(); GlStateManager.enableTexture2D(); } - + this.drawSlot(j1, p_148120_1_, k1, l1, p_148120_3_, p_148120_4_); } } @@ -88,8 +90,8 @@ public class GuiReplayListExtended extends GuiListExtended { entries = new ArrayList(); } - public void addEntry(String fileName, ReplayMetaData metaData) { - entries.add(new GuiReplayListEntry(this, fileName, metaData)); + public void addEntry(String fileName, ReplayMetaData metaData, BufferedImage image) { + entries.add(new GuiReplayListEntry(this, fileName, metaData, image)); } @Override diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayManager.java b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayManager.java similarity index 79% rename from src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayManager.java rename to src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayManager.java index a8ac712f..a9a20629 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplayManager.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/GuiReplayManager.java @@ -1,5 +1,11 @@ -package eu.crushedpixel.replaymod.gui; +package eu.crushedpixel.replaymod.gui.replaymanager; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.Unpooled; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -12,10 +18,13 @@ import java.util.Comparator; import java.util.Date; import java.util.List; +import javax.imageio.ImageIO; + import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiYesNo; import net.minecraft.client.gui.GuiYesNoCallback; +import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.client.resources.I18n; import net.minecraft.util.Util; @@ -28,9 +37,11 @@ import org.lwjgl.input.Keyboard; import com.google.gson.Gson; import com.mojang.realmsclient.util.Pair; +import eu.crushedpixel.replaymod.gui.GuiReplaySettings; import eu.crushedpixel.replaymod.recording.ConnectionEventHandler; import eu.crushedpixel.replaymod.recording.ReplayMetaData; import eu.crushedpixel.replaymod.replay.ReplayHandler; +import eu.crushedpixel.replaymod.utils.ImageUtils; public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { @@ -41,12 +52,12 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { private String hoveringText; private boolean initialized; private GuiReplayListExtended replayGuiList; - private List> replayFileList = new ArrayList>(); + private List, BufferedImage>> replayFileList = new ArrayList, BufferedImage>>(); private GuiButton loadButton, folderButton, renameButton, deleteButton, cancelButton, settingsButton; private static Gson gson = new Gson(); private boolean replaying = false; - + private static final int LOAD_BUTTON_ID = 9001; private static final int FOLDER_BUTTON_ID = 9002; private static final int RENAME_BUTTON_ID = 9003; @@ -55,14 +66,14 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { private static final int CANCEL_BUTTON_ID = 9006; private boolean delete_file = false; - + private void reloadFiles() { replayGuiList.clearEntries(); - replayFileList = new ArrayList>(); + replayFileList = new ArrayList, BufferedImage>>(); File folder = new File("./replay_recordings/"); folder.mkdirs(); - + for(File file : folder.listFiles()) { if(("."+FilenameUtils.getExtension(file.getAbsolutePath())).equals(ConnectionEventHandler.ZIP_FILE_EXTENSION)) { try { @@ -70,14 +81,33 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { ZipArchiveEntry recfile = archive.getEntry("recording"+ConnectionEventHandler.TEMP_FILE_EXTENSION); ZipArchiveEntry metadata = archive.getEntry("metaData"+ConnectionEventHandler.JSON_FILE_EXTENSION); + ZipArchiveEntry image = archive.getEntry("thumb"); + BufferedImage img = null; + if(image != null) { + InputStream is = archive.getInputStream(image); + is.skip(7); + BufferedImage bimg = ImageIO.read(is); + if(bimg != null) { + img = ImageUtils.scaleImage(bimg, new Dimension(1280, 720)); + } + + /* Old way of reading thumbnail + else { + is = archive.getInputStream(image); + bimg = ImageIO.read(is); + img = ImageUtils.scaleImage(bimg, new Dimension(1280, 720)); + } + */ + } + InputStream is = archive.getInputStream(metadata); BufferedReader br = new BufferedReader(new InputStreamReader(is)); - + String json = br.readLine(); - + ReplayMetaData metaData = gson.fromJson(json, ReplayMetaData.class); - - replayFileList.add(Pair.of(file, metaData)); + + replayFileList.add(Pair.of(Pair.of(file, metaData), img)); archive.close(); } catch(Exception e) { @@ -85,21 +115,25 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { } } } - + Collections.sort(replayFileList, new FileAgeComparator()); - - for(Pair p : replayFileList) { - replayGuiList.addEntry(FilenameUtils.getBaseName(p.first().getName()), p.second()); + + for(Pair, BufferedImage> p : replayFileList) { + replayGuiList.addEntry(FilenameUtils.getBaseName(p.first().first().getName()), p.first().second(), p.second()); } } - - public class FileAgeComparator implements Comparator> { + + public class FileAgeComparator implements Comparator, BufferedImage>> { @Override - public int compare(Pair o1, Pair o2) { - return (int)(new Date(o2.second().getDate()).compareTo(new Date(o1.second().getDate()))); + public int compare(Pair, BufferedImage> o1, Pair, BufferedImage> o2) { + try { + return (int)(new Date(o2.first().second().getDate()).compareTo(new Date(o1.first().second().getDate()))); + } catch(Exception e) { + return 0; + } } - + } public void initGui() @@ -183,7 +217,7 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { } else if(button.id == RENAME_BUTTON_ID) { - File file = replayFileList.get(replayGuiList.selected).first(); + File file = replayFileList.get(replayGuiList.selected).first().first(); this.mc.displayGuiScreen(new GuiRenameReplay(this, file)); } else if(button.id == FOLDER_BUTTON_ID) @@ -241,7 +275,7 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { if (result) { - replayFileList.get(replayGuiList.selected).first().delete(); + replayFileList.get(replayGuiList.selected).first().first().delete(); replayFileList.remove(replayGuiList.selected); } @@ -265,16 +299,15 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback { deleteButton.enabled = b; } - public void loadReplay(int id) - { - mc.displayGuiScreen((GuiScreen)null); + public void loadReplay(int id) { + mc.displayGuiScreen((GuiScreen)null); - try { - ReplayHandler.startReplay(replayFileList.get(id).first()); + try { + ReplayHandler.startReplay(replayFileList.get(id).first().first()); } catch(Exception e) { e.printStackTrace(); } - } + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/ResourceHelper.java b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/ResourceHelper.java new file mode 100644 index 00000000..b4d2cd8e --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replaymanager/ResourceHelper.java @@ -0,0 +1,24 @@ +package eu.crushedpixel.replaymod.gui.replaymanager; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; + +public class ResourceHelper { + + private static List openResources = new ArrayList(); + + public static void registerResource(ResourceLocation loc) { + openResources.add(loc); + } + + public static void freeResources() { + for(ResourceLocation loc : openResources) { + Minecraft.getMinecraft().getTextureManager().deleteTexture(loc); + } + + openResources = new ArrayList(); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/recording/ConnectionEventHandler.java b/src/main/java/eu/crushedpixel/replaymod/recording/ConnectionEventHandler.java index dc5efb08..82480086 100644 --- a/src/main/java/eu/crushedpixel/replaymod/recording/ConnectionEventHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/recording/ConnectionEventHandler.java @@ -39,8 +39,6 @@ public class ConnectionEventHandler { private static PacketListener packetListener = null; private static boolean isRecording = false; - - public static boolean saving = false; public static boolean isRecording() { return isRecording; diff --git a/src/main/java/eu/crushedpixel/replaymod/recording/DataListener.java b/src/main/java/eu/crushedpixel/replaymod/recording/DataListener.java index 70bccc8d..58c8c921 100644 --- a/src/main/java/eu/crushedpixel/replaymod/recording/DataListener.java +++ b/src/main/java/eu/crushedpixel/replaymod/recording/DataListener.java @@ -23,6 +23,7 @@ import net.minecraft.client.Minecraft; import com.google.gson.Gson; +import eu.crushedpixel.replaymod.gui.GuiReplaySaving; import eu.crushedpixel.replaymod.holders.PacketData; public abstract class DataListener extends ChannelInboundHandlerAdapter { @@ -144,7 +145,7 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter { byte[] buffer = new byte[1024]; try { - ConnectionEventHandler.saving = true; + GuiReplaySaving.replaySaving = true; String mcversion = Minecraft.getMinecraft().getVersion(); String[] pl = players.toArray(new String[players.size()]); @@ -181,10 +182,10 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter { file.delete(); - ConnectionEventHandler.saving = false; + GuiReplaySaving.replaySaving = false; } catch(Exception e) { e.printStackTrace(); - ConnectionEventHandler.saving = false; + GuiReplaySaving.replaySaving = false; } } diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java index 4ad0f1f6..f44d90eb 100644 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java @@ -389,4 +389,11 @@ public class ReplayHandler { public static Position getLastPosition() { return lastPosition; } + + public static File getReplayFile() { + if(replaySender != null) { + return replaySender.getReplayFile(); + } + return null; + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java index a5683cf4..68efad6e 100644 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java @@ -523,4 +523,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { //return timeInfo.get().getSpeed(); } + public File getReplayFile() { + return replayFile; + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/screenshot/ReplayScreenshot.java b/src/main/java/eu/crushedpixel/replaymod/screenshot/ReplayScreenshot.java new file mode 100644 index 00000000..7e8e4f0a --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/screenshot/ReplayScreenshot.java @@ -0,0 +1,234 @@ +package eu.crushedpixel.replaymod.screenshot; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.IntBuffer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import javax.imageio.ImageIO; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.texture.TextureUtil; +import net.minecraft.client.shader.Framebuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import eu.crushedpixel.replaymod.chat.ChatMessageRequests; +import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType; +import eu.crushedpixel.replaymod.gui.GuiReplaySaving; +import eu.crushedpixel.replaymod.replay.ReplayHandler; +import eu.crushedpixel.replaymod.utils.ImageUtils; + +public class ReplayScreenshot { + + private static Minecraft mc = Minecraft.getMinecraft(); + private static IntBuffer pixelBuffer; + private static int[] pixelValues; + + private static final byte[] uniqueBytes = new byte[]{0,1,1,2,3,5,8}; + + private static long last_finish = -1; + + private static boolean before; + private static double beforeSpeed; + private static GuiScreen beforeScreen; + + public static void prepareScreenshot() { + before = mc.gameSettings.hideGUI; + beforeSpeed = ReplayHandler.getSpeed(); + beforeScreen = mc.currentScreen; + } + + public static void saveScreenshot(Framebuffer buffer) + { + + try { + GuiReplaySaving.replaySaving = true; + + mc.gameSettings.hideGUI = true; + mc.currentScreen = null; + + mc.entityRenderer.updateCameraAndRender(0); + ReplayHandler.setSpeed(0); + + int width = mc.displayWidth; + int height = mc.displayHeight; + + if (OpenGlHelper.isFramebufferEnabled()) + { + width = buffer.framebufferTextureWidth; + height = buffer.framebufferTextureHeight; + } + + int k = width * height; + + if (pixelBuffer == null || pixelBuffer.capacity() < k) + { + pixelBuffer = BufferUtils.createIntBuffer(k); + pixelValues = new int[k]; + } + + GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + pixelBuffer.clear(); + + if (OpenGlHelper.isFramebufferEnabled()) { + GlStateManager.bindTexture(buffer.framebufferTexture); + GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer); + } + else { + GL11.glReadPixels(0, 0, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer); + } + + pixelBuffer.get(pixelValues); + TextureUtil.processPixelValues(pixelValues, width, height); + BufferedImage bufferedimage = null; + + + if (OpenGlHelper.isFramebufferEnabled()) + { + bufferedimage = new BufferedImage(buffer.framebufferWidth, buffer.framebufferHeight, 1); + int l = buffer.framebufferTextureHeight - buffer.framebufferHeight; + + for (int i1 = l; i1 < buffer.framebufferTextureHeight; ++i1) + { + for (int j1 = 0; j1 < buffer.framebufferWidth; ++j1) + { + bufferedimage.setRGB(j1, i1 - l, pixelValues[i1 * buffer.framebufferTextureWidth + j1]); + } + } + } + else { + bufferedimage = new BufferedImage(width, height, 1); + bufferedimage.setRGB(0, 0, width, height, pixelValues, 0, width); + } + + final BufferedImage fbi = bufferedimage; + + mc.gameSettings.hideGUI = before; + mc.currentScreen = beforeScreen; + ReplayHandler.setSpeed(beforeSpeed); + + //The actual cropping and saving should be executed in a separate thread + Thread ioThread = new Thread(new Runnable() { + + @Override + public void run() { + try { + + + float aspect = 1280f/720f; + + Rectangle rect; + if((float)fbi.getWidth()/(float)fbi.getHeight() <= aspect) { + int h = Math.round(fbi.getWidth()/aspect); + int y = (fbi.getHeight()/2) - (h/2); + System.out.println(h+" | "+y); + rect = new Rectangle(0, y, fbi.getWidth(), h); + } else { + int w = Math.round(fbi.getHeight()*aspect); + int x = (fbi.getWidth()/2) - (w/2); + rect = new Rectangle(x, 0, w, fbi.getHeight()); + } + + final BufferedImage nbi = ImageUtils.cropImage(fbi, rect); + + File replayFile = ReplayHandler.getReplayFile(); + + File folder = new File("./replay_recordings/"); + folder.mkdirs(); + + File temp = File.createTempFile("thumb", null); + + int h = 720; + int w = 1280; + //int w = width*(h/height); + + BufferedImage img = ImageUtils.scaleImage(nbi, new Dimension(w, h)); + + ImageIO.write(img, "jpg", temp); + + File tempFile = File.createTempFile(replayFile.getName(), null); + tempFile.delete(); tempFile.deleteOnExit(); + + replayFile.renameTo(tempFile); + + replayFile.delete(); + replayFile.createNewFile(); + + byte[] buf = new byte[1024]; + + ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile)); + ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(replayFile)); + + FileInputStream fis = new FileInputStream(temp); + + ZipEntry entry = zin.getNextEntry(); + while (entry != null) { + String name = entry.getName(); + + if(!name.contains("thumb")) { + // Add ZIP entry to output stream. + zout.putNextEntry(new ZipEntry(name)); + // Transfer bytes from the ZIP file to the output file + int len; + while ((len = zin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + } + + entry = zin.getNextEntry(); + } + + zout.putNextEntry(new ZipEntry("thumb")); + int len; + //Add unique bytes to the end of the file + zout.write(uniqueBytes); + + while ((len = fis.read(buf)) > 0) { + zout.write(buf, 0, len); + } + + // Close the streams + fis.close(); + zin.close(); + // Compress the files + // Complete the ZIP file + zout.close(); + tempFile.delete(); + temp.delete(); + + ChatMessageRequests.addChatMessage("Thumbnail has been successfully saved", ChatMessageType.INFORMATION); + } catch(Exception e) {} + finally { + GuiReplaySaving.replaySaving = false; + } + } + }); + + ioThread.start(); + } + catch (Exception exception) { + mc.gameSettings.hideGUI = before; + mc.currentScreen = beforeScreen; + ReplayHandler.setSpeed(beforeSpeed); + exception.printStackTrace(); + ChatMessageRequests.addChatMessage("Thumbnail could not be saved", ChatMessageType.WARNING); + } + + last_finish = System.currentTimeMillis(); + } + + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java b/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java index 682bb776..a09394d4 100644 --- a/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java +++ b/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java @@ -20,9 +20,9 @@ public class ReplaySettings { private boolean showNotifications = true; private boolean forceLinearPath = false; private boolean lightingEnabled = false; - + private static Field mcTimer; - + static { try { mcTimer = Minecraft.class.getDeclaredField(MCPNames.field("field_71428_T")); @@ -89,13 +89,17 @@ public class ReplaySettings { Minecraft.getMinecraft().gameSettings.setOptionFloatValue(Options.GAMMA, ReplayHandler.getInitialGamma()); } try { - Timer timer = (Timer)mcTimer.get(Minecraft.getMinecraft()); - timer.elapsedPartialTicks++; - timer.renderPartialTicks++; + if(ReplayHandler.isPaused()) { + Timer timer = (Timer)mcTimer.get(Minecraft.getMinecraft()); + timer.elapsedPartialTicks++; + timer.renderPartialTicks++; + } else { + Minecraft.getMinecraft().entityRenderer.updateCameraAndRender(0); + } } catch (Exception e) { e.printStackTrace(); } - + rewriteSettings(); } diff --git a/src/main/java/eu/crushedpixel/replaymod/utils/ImageUtils.java b/src/main/java/eu/crushedpixel/replaymod/utils/ImageUtils.java new file mode 100644 index 00000000..f171e390 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/utils/ImageUtils.java @@ -0,0 +1,69 @@ +package eu.crushedpixel.replaymod.utils; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +public class ImageUtils { + + public static BufferedImage scaleImage(BufferedImage img, Dimension d) { + img = scaleByHalf(img, d); + img = scaleExact(img, d); + return img; + } + + private static BufferedImage scaleByHalf(BufferedImage img, Dimension d) { + int w = img.getWidth(); + int h = img.getHeight(); + float factor = getBinFactor(w, h, d); + + // make new size + w *= factor; + h *= factor; + BufferedImage scaled = new BufferedImage(w, h, + BufferedImage.TYPE_INT_RGB); + Graphics2D g = scaled.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g.drawImage(img, 0, 0, w, h, null); + g.dispose(); + return scaled; + } + + private static BufferedImage scaleExact(BufferedImage img, Dimension d) { + float factor = getFactor(img.getWidth(), img.getHeight(), d); + + // create the image + int w = (int) (img.getWidth() * factor); + int h = (int) (img.getHeight() * factor); + BufferedImage scaled = new BufferedImage(w, h, + BufferedImage.TYPE_INT_RGB); + + Graphics2D g = scaled.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(img, 0, 0, w, h, null); + g.dispose(); + return scaled; + } + + static float getBinFactor(int width, int height, Dimension dim) { + float factor = 1; + float target = getFactor(width, height, dim); + if (target <= 1) { while (factor / 2 > target) { factor /= 2; } + } else { while (factor * 2 < target) { factor *= 2; } } + return factor; + } + + static float getFactor(int width, int height, Dimension dim) { + float sx = dim.width / (float) width; + float sy = dim.height / (float) height; + return Math.min(sx, sy); + } + + public static BufferedImage cropImage(BufferedImage src, Rectangle rect) { + return src.getSubimage(rect.x, rect.y, rect.width, rect.height); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/api/client/StreamTools.java b/src/main/java/eu/crushedpixel/replaymod/utils/StreamTools.java similarity index 96% rename from src/main/java/eu/crushedpixel/replaymod/api/client/StreamTools.java rename to src/main/java/eu/crushedpixel/replaymod/utils/StreamTools.java index 6a9fa82a..64142d5a 100644 --- a/src/main/java/eu/crushedpixel/replaymod/api/client/StreamTools.java +++ b/src/main/java/eu/crushedpixel/replaymod/utils/StreamTools.java @@ -15,7 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -package eu.crushedpixel.replaymod.api.client; +package eu.crushedpixel.replaymod.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/eu/crushedpixel/replaymod/utils/ZipFileUtils.java b/src/main/java/eu/crushedpixel/replaymod/utils/ZipFileUtils.java new file mode 100644 index 00000000..e58afab6 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/utils/ZipFileUtils.java @@ -0,0 +1,59 @@ +package eu.crushedpixel.replaymod.utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class ZipFileUtils { + + public static void deleteZipEntry(File zipFile, + String[] files) throws IOException { + // get a temp file + File tempFile = File.createTempFile(zipFile.getName(), null); + // delete it, otherwise you cannot rename your existing zip to it. + tempFile.delete(); + tempFile.deleteOnExit(); + boolean renameOk=zipFile.renameTo(tempFile); + if (!renameOk) + { + throw new RuntimeException("could not rename the file "+zipFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath()); + } + byte[] buf = new byte[1024]; + + ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile)); + ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zipFile)); + + ZipEntry entry = zin.getNextEntry(); + while (entry != null) { + String name = entry.getName(); + boolean toBeDeleted = false; + for (String f : files) { + if (f.equals(name)) { + toBeDeleted = true; + break; + } + } + if (!toBeDeleted) { + // Add ZIP entry to output stream. + zout.putNextEntry(new ZipEntry(name)); + // Transfer bytes from the ZIP file to the output file + int len; + while ((len = zin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + } + entry = zin.getNextEntry(); + } + // Close the streams + zin.close(); + // Compress the files + // Complete the ZIP file + zout.close(); + tempFile.delete(); + } + +}