Cleaned up even more GUIs

Added possibility to create Thumbnails and show them in the Replay Manager
This commit is contained in:
Marius Metzger
2015-01-25 20:36:02 +01:00
parent f39e0f9eae
commit 8c2cf9011e
23 changed files with 663 additions and 206 deletions

View File

@@ -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);

View File

@@ -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 {
@@ -65,19 +68,21 @@ public class ApiClient {
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) {}
}
}

View File

@@ -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");

View File

@@ -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<Class> allowedGUIs = new ArrayList<Class>() {
{
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<GuiButton>(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<GuiButton>)event.buttonList) {
@@ -74,13 +97,27 @@ public class GuiEventHandler {
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();
}
}

View File

@@ -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,7 +244,7 @@ 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);
}
@@ -783,12 +823,13 @@ 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());
}
@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -1,4 +1,4 @@
package eu.crushedpixel.replaymod.gui;
package eu.crushedpixel.replaymod.gui.replaymanager;
import java.io.File;
import java.io.IOException;

View File

@@ -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 {
@@ -23,7 +29,10 @@ 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<String> list = new ArrayList<String>();
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

View File

@@ -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;
@@ -88,8 +90,8 @@ public class GuiReplayListExtended extends GuiListExtended {
entries = new ArrayList<GuiReplayListEntry>();
}
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

View File

@@ -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,7 +52,7 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback {
private String hoveringText;
private boolean initialized;
private GuiReplayListExtended replayGuiList;
private List<Pair<File, ReplayMetaData>> replayFileList = new ArrayList<Pair<File, ReplayMetaData>>();
private List<Pair<Pair<File, ReplayMetaData>, BufferedImage>> replayFileList = new ArrayList<Pair<Pair<File, ReplayMetaData>, BufferedImage>>();
private GuiButton loadButton, folderButton, renameButton, deleteButton, cancelButton, settingsButton;
private static Gson gson = new Gson();
@@ -58,7 +69,7 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback {
private void reloadFiles() {
replayGuiList.clearEntries();
replayFileList = new ArrayList<Pair<File, ReplayMetaData>>();
replayFileList = new ArrayList<Pair<Pair<File, ReplayMetaData>, BufferedImage>>();
File folder = new File("./replay_recordings/");
folder.mkdirs();
@@ -70,6 +81,25 @@ 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));
@@ -77,7 +107,7 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback {
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) {
@@ -88,16 +118,20 @@ public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback {
Collections.sort(replayFileList, new FileAgeComparator());
for(Pair<File, ReplayMetaData> p : replayFileList) {
replayGuiList.addEntry(FilenameUtils.getBaseName(p.first().getName()), p.second());
for(Pair<Pair<File, ReplayMetaData>, BufferedImage> p : replayFileList) {
replayGuiList.addEntry(FilenameUtils.getBaseName(p.first().first().getName()), p.first().second(), p.second());
}
}
public class FileAgeComparator implements Comparator<Pair<File, ReplayMetaData>> {
public class FileAgeComparator implements Comparator<Pair<Pair<File, ReplayMetaData>, BufferedImage>> {
@Override
public int compare(Pair<File, ReplayMetaData> o1, Pair<File, ReplayMetaData> o2) {
return (int)(new Date(o2.second().getDate()).compareTo(new Date(o1.second().getDate())));
public int compare(Pair<Pair<File, ReplayMetaData>, BufferedImage> o1, Pair<Pair<File, ReplayMetaData>, BufferedImage> o2) {
try {
return (int)(new Date(o2.first().second().getDate()).compareTo(new Date(o1.first().second().getDate())));
} catch(Exception e) {
return 0;
}
}
}
@@ -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();
}
}
}
}

View File

@@ -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<ResourceLocation> openResources = new ArrayList<ResourceLocation>();
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<ResourceLocation>();
}
}

View File

@@ -40,8 +40,6 @@ public class ConnectionEventHandler {
private static boolean isRecording = false;
public static boolean saving = false;
public static boolean isRecording() {
return isRecording;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -523,4 +523,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
//return timeInfo.get().getSpeed();
}
public File getReplayFile() {
return replayFile;
}
}

View File

@@ -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();
}
}

View File

@@ -89,9 +89,13 @@ 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();
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}
}