diff --git a/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java b/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java index c56a3531..8a54b0d6 100755 --- a/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java +++ b/src/main/java/eu/crushedpixel/replaymod/ReplayMod.java @@ -55,7 +55,7 @@ public class ReplayMod public static GuiReplayOverlay overlay = new GuiReplayOverlay(); - public static ReplaySettings replaySettings = new ReplaySettings(true, true, true, false, false, 30, 0.5f); + public static ReplaySettings replaySettings; public static Configuration config; public static boolean firstMainMenu = true; diff --git a/src/main/java/eu/crushedpixel/replaymod/events/KeyInputHandler.java b/src/main/java/eu/crushedpixel/replaymod/events/KeyInputHandler.java index 6baecfa1..5236f669 100755 --- a/src/main/java/eu/crushedpixel/replaymod/events/KeyInputHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/KeyInputHandler.java @@ -82,7 +82,6 @@ public class KeyInputHandler { //Custom registered handlers if(kb.getKeyDescription().equals(KeybindRegistry.KEY_THUMBNAIL) && kb.isPressed() && !found) { - System.out.println("thumbnail key pressed"); ReplayScreenshot.prepareScreenshot(); GuiReplayOverlay.requestScreenshot(); } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySettings.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySettings.java index 3ae0f70f..0b2d98c5 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySettings.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiReplaySettings.java @@ -2,20 +2,14 @@ package eu.crushedpixel.replaymod.gui; import java.awt.Color; import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Map; import java.util.Map.Entry; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiOptionButton; -import net.minecraft.client.gui.GuiOptionSlider; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.I18n; import net.minecraftforge.fml.client.FMLClientHandler; import eu.crushedpixel.replaymod.ReplayMod; -import eu.crushedpixel.replaymod.reflection.MCPNames; -import eu.crushedpixel.replaymod.settings.ReplaySettings; +import eu.crushedpixel.replaymod.settings.ReplaySettings.Option; public class GuiReplaySettings extends GuiScreen { @@ -30,8 +24,9 @@ public class GuiReplaySettings extends GuiScreen { private static final int FORCE_LINEAR = 9007; private static final int ENABLE_LIGHTING = 9008; private static final int FRAMERATE_SLIDER_ID = 9009; + private static final int RESOURCEPACK_ID = 9010; - private GuiButton recordServerButton, recordSPButton, sendChatButton, linearButton, lightingButton; + private GuiButton recordServerButton, recordSPButton, sendChatButton, linearButton, lightingButton, resourcePackButton; public GuiReplaySettings(GuiScreen parentGuiScreen) { @@ -43,31 +38,48 @@ public class GuiReplaySettings extends GuiScreen { this.buttonList.clear(); this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height - 27, I18n.format("gui.done", new Object[0]))); - Map aoptions = ReplayMod.replaySettings.getOptions(); + Option[] aoptions = Option.values(); int k = 0; int i = 0; - for (Entry e : aoptions.entrySet()) { + for (Option o : aoptions) { - if(e.getKey().equals("Video Quality")) { - this.buttonList.add(new GuiVideoQualitySlider(QUALITY_SLIDER_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), (Float)e.getValue(), "Video Quality")); - } else if(e.getKey().equals("Video Framerate")) { - this.buttonList.add(new GuiVideoFramerateSlider(FRAMERATE_SLIDER_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), (Integer)e.getValue(), "Video Framerate")); - } else if(e.getKey().equals("Enable Notifications")) { - sendChatButton = new GuiButton(SEND_CHAT, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Enable Notifications: "+onOff((Boolean)e.getValue())); - this.buttonList.add(sendChatButton); - } else if(e.getKey().equals("Record Server")) { - recordServerButton = new GuiButton(RECORDSERVER_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Record Server: "+onOff((Boolean)e.getValue())); - this.buttonList.add(recordServerButton); - } else if(e.getKey().equals("Record Singleplayer")) { - recordSPButton = new GuiButton(RECORDSP_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Record Singleplayer: "+onOff((Boolean)e.getValue())); - this.buttonList.add(recordSPButton); - } else if(e.getKey().equals("Force Linear Movement")) { - linearButton = new GuiButton(FORCE_LINEAR, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Camera Path: "+linearOnOff((Boolean)e.getValue())); - this.buttonList.add(linearButton); - } else if(e.getKey().equals("Enable Lighting")) { - lightingButton = new GuiButton(ENABLE_LIGHTING, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Enable Lighting: "+onOff((Boolean)e.getValue())); - this.buttonList.add(lightingButton); + switch(o) { + case lighting: + this.buttonList.add(lightingButton = new GuiButton(ENABLE_LIGHTING, this.width / 2 - 155 + i % 2 * 160, + this.height / 6 + 24 * (i >> 1), 150, 20, "Enable Lighting: "+onOff((Boolean)o.getValue()))); + break; + case linear: + this.buttonList.add(linearButton = new GuiButton(FORCE_LINEAR, this.width / 2 - 155 + i % 2 * 160, + this.height / 6 + 24 * (i >> 1), 150, 20, "Camera Path: "+linearOnOff((Boolean)o.getValue()))); + break; + case notifications: + this.buttonList.add(sendChatButton = new GuiButton(SEND_CHAT, + this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, + "Enable Notifications: "+onOff((Boolean)o.getValue()))); + break; + case recordServer: + this.buttonList.add(recordServerButton = new GuiButton(RECORDSERVER_ID, + this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Record Server: "+onOff((Boolean)o.getValue()))); + break; + case recordSingleplayer: + this.buttonList.add(recordSPButton = new GuiButton(RECORDSP_ID, this.width / 2 - 155 + i % 2 * 160, + this.height / 6 + 24 * (i >> 1), 150, 20, "Record Singleplayer: "+onOff((Boolean)o.getValue()))); + break; + case useResources: + this.buttonList.add(resourcePackButton = new GuiButton(RESOURCEPACK_ID, this.width / 2 - 155 + i % 2 * 160, + this.height / 6 + 24 * (i >> 1), 150, 20, "Server Resource Packs: "+onOff((Boolean)o.getValue()))); + break; + case videoFramerate: + this.buttonList.add(new GuiVideoFramerateSlider(FRAMERATE_SLIDER_ID, + this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), (Integer)o.getValue(), "Video Framerate")); + break; + case videoQuality: + this.buttonList.add(new GuiVideoQualitySlider(QUALITY_SLIDER_ID, + this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), (Float)o.getValue(), "Video Quality")); + break; + default: + break; } ++i; @@ -136,6 +148,12 @@ public class GuiReplaySettings extends GuiScreen { lightingButton.displayString = "Enable Lighting: "+onOff(enabled); ReplayMod.replaySettings.setLightingEnabled(enabled); break; + case RESOURCEPACK_ID: + enabled = ReplayMod.replaySettings.getUseResourcePacks(); + enabled = !enabled; + resourcePackButton.displayString = "Server Resource Packs: "+onOff(enabled); + ReplayMod.replaySettings.setUseResourcePacks(enabled); + break; } } } diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java index 5b80541e..9a7b9d40 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java @@ -12,15 +12,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; +import java.net.URL; import java.util.ArrayList; -import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiDownloadTerrain; import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.resources.ResourcePackRepository; import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.network.EnumConnectionState; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; @@ -32,8 +31,6 @@ import net.minecraft.network.play.server.S07PacketRespawn; import net.minecraft.network.play.server.S08PacketPlayerPosLook; import net.minecraft.network.play.server.S0BPacketAnimation; import net.minecraft.network.play.server.S0CPacketSpawnPlayer; -import net.minecraft.network.play.server.S0EPacketSpawnObject; -import net.minecraft.network.play.server.S0FPacketSpawnMob; import net.minecraft.network.play.server.S1CPacketEntityMetadata; import net.minecraft.network.play.server.S1DPacketEntityEffect; import net.minecraft.network.play.server.S1FPacketSetExperience; @@ -59,9 +56,9 @@ import net.minecraftforge.fml.client.FMLClientHandler; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; -import com.google.common.base.Predicate; import com.google.gson.Gson; import eu.crushedpixel.replaymod.ReplayMod; @@ -144,7 +141,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { e.printStackTrace(); } } - + public long getDesiredTimestamp() { return desiredTimeStamp; } @@ -346,7 +343,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { private ArrayList badPackets = new ArrayList() { { add(S28PacketEffect.class); - add(S48PacketResourcePackSend.class); add(S2BPacketChangeGameState.class); add(S06PacketUpdateHealth.class); add(S2DPacketOpenWindow.class); @@ -368,6 +364,98 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { //private static Field dataWatcherField; + private static class ResourcePackCheck extends Thread { + + public ResourcePackCheck(String url, String hash) { + this.url = url; + this.hash = hash; + } + + private String url, hash; + + private static Field serverResourcePackDirectory; + private static Minecraft mc = Minecraft.getMinecraft(); + private static ResourcePackRepository repo = mc.getResourcePackRepository(); + + static { + try { + serverResourcePackDirectory = ResourcePackRepository.class.getDeclaredField(MCPNames.field("field_148534_e")); + serverResourcePackDirectory.setAccessible(true); + } catch(Exception e) { + e.printStackTrace(); + } + } + + private File getServerResourcePackLocation(String url, String hash) throws IOException, IllegalArgumentException, IllegalAccessException { + + String filename; + + if (hash.matches("^[a-f0-9]{40}$")) { + filename = hash; + } else { + filename = url.substring(url.lastIndexOf("/") + 1); + + if (filename.contains("?")) + { + filename = filename.substring(0, filename.indexOf("?")); + } + + if (!filename.endsWith(".zip")) + { + return null; + } + + filename = "legacy_" + filename.replaceAll("\\W", ""); + } + + File folder = (File)serverResourcePackDirectory.get(repo); + File rp = new File(folder, filename); + + return rp; + } + + private boolean downloadServerResourcePack(String url, File file) { + try { + FileUtils.copyURLToFile(new URL(url), file); + return true; + } catch(Exception e) { + e.printStackTrace(); + } + return false; + } + + @Override + public void run() { + try { + boolean use = ReplayMod.instance.replaySettings.getUseResourcePacks(); + if(!use) return; + + System.out.println("Looking for downloaded Resource Pack..."); + File rp = getServerResourcePackLocation(url, hash); + if(rp == null) { + System.out.println("Invalid Resource Pack provided"); + return; + } + if(rp.exists()) { + System.out.println("Resource Pack found!"); + repo.func_177319_a(rp); + + } else { + System.out.println("No Resource Pack found."); + System.out.println("Attempting to download Resource Pack..."); + boolean success = downloadServerResourcePack(url, rp); + System.out.println(success ? "Resource pack was successfully downloaded!" : "Resource Pack download failed."); + if(success) { + repo.func_177319_a(rp); + } + } + + } catch(Exception e) { + e.printStackTrace(); + } + } + } + static { try { playerUUIDField = S0CPacketSpawnPlayer.class.getDeclaredField(MCPNames.field("field_179820_b")); @@ -402,7 +490,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { try { Packet p = ReplayFileIO.deserializePacket(ba); - + if(p == null) return; //If hurrying, ignore some packets, unless during Replay Path and *not* in initial hurry @@ -419,6 +507,14 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { p = TimeHandler.getTimePacket((S03PacketTimeUpdate)p); } + if(p instanceof S48PacketResourcePackSend) { + S48PacketResourcePackSend pa = (S48PacketResourcePackSend)p; + Thread t = new ResourcePackCheck(pa.func_179783_a(), pa.func_179784_b()); + t.start(); + + return; + } + if(p instanceof S02PacketChat) { byte pos = (Byte)chatPacketPosition.get(p); if(pos == 1) { //Ignores command block output sent @@ -442,7 +538,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } } } - */ + */ try { if(p instanceof S1CPacketEntityMetadata) { diff --git a/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java b/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java index 1e6e530a..8a0460a2 100755 --- a/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java +++ b/src/main/java/eu/crushedpixel/replaymod/settings/ReplaySettings.java @@ -1,29 +1,35 @@ package eu.crushedpixel.replaymod.settings; import java.lang.reflect.Field; -import java.util.LinkedHashMap; -import java.util.Map; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiOptions; -import net.minecraft.client.gui.GuiVideoSettings; import net.minecraft.client.settings.GameSettings.Options; -import net.minecraft.entity.player.EnumPlayerModelParts; import net.minecraft.util.Timer; -import net.minecraftforge.common.config.Property; +import net.minecraftforge.common.config.Configuration; import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.reflection.MCPNames; import eu.crushedpixel.replaymod.replay.ReplayHandler; public class ReplaySettings { - private boolean enableRecordingServer = true; - private boolean enableRecordingSingleplayer = true; - private boolean showNotifications = true; - private boolean forceLinearPath = false; - private boolean lightingEnabled = false; - private float videoQuality = 0.5f; - private int videoFramerate = 30; + public enum Option { + recordServer(true), recordSingleplayer(true), notifications(true), linear(false), + lighting(false), useResources(true), videoQuality(0.5f), videoFramerate(30); + + private Object value; + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + Option(Object value) { + this.value = value; + } + } private static Field mcTimer; @@ -39,63 +45,71 @@ public class ReplaySettings { public ReplaySettings(boolean enableRecordingServer, boolean enableRecordingSingleplayer, boolean showNotifications, boolean forceLinearPath, boolean lightingEnabled, int framerate, float videoQuality) { - this.enableRecordingServer = enableRecordingServer; - this.enableRecordingSingleplayer = enableRecordingSingleplayer; - this.showNotifications = showNotifications; - this.forceLinearPath = forceLinearPath; - this.lightingEnabled = lightingEnabled; - this.videoFramerate = Math.min(120, Math.max(10, framerate)); - this.videoQuality = Math.min(0.9f, Math.max(0.1f, videoQuality)); + setEnableRecordingServer(enableRecordingServer); + setEnableRecordingSingleplayer(enableRecordingSingleplayer); + setLinearMovement(forceLinearPath); + setShowNotifications(showNotifications); + setLightingEnabled(lightingEnabled); + setVideoFramerate(Math.min(120, Math.max(10, framerate))); + setVideoQuality(Math.min(0.9f, Math.max(0.1f, videoQuality))); } public int getVideoFramerate() { - return videoFramerate; + return (Integer)Option.videoFramerate.getValue(); } public void setVideoFramerate(int framerate) { - this.videoFramerate = Math.min(120, Math.max(10, framerate)); + Option.videoFramerate.setValue(Math.min(120, Math.max(10, framerate))); rewriteSettings(); } public float getVideoQuality() { - return videoQuality; + return (Float)Option.videoQuality.getValue(); } public void setVideoQuality(float videoQuality) { - this.videoQuality = Math.min(0.9f, Math.max(0.1f, videoQuality)); + Option.videoQuality.setValue(Math.min(0.9f, Math.max(0.1f, videoQuality))); rewriteSettings(); } public boolean isEnableRecordingServer() { - return enableRecordingServer; + return (Boolean)Option.recordServer.getValue(); } public void setEnableRecordingServer(boolean enableRecordingServer) { - this.enableRecordingServer = enableRecordingServer; + Option.recordServer.setValue(enableRecordingServer); rewriteSettings(); } public boolean isEnableRecordingSingleplayer() { - return enableRecordingSingleplayer; + return (Boolean)Option.recordSingleplayer.getValue(); } public void setEnableRecordingSingleplayer(boolean enableRecordingSingleplayer) { - this.enableRecordingSingleplayer = enableRecordingSingleplayer; + Option.recordSingleplayer.setValue(enableRecordingSingleplayer); rewriteSettings(); } public boolean isShowNotifications() { - return showNotifications; + return (Boolean)Option.notifications.getValue(); } public void setShowNotifications(boolean showNotifications) { - this.showNotifications = showNotifications; + Option.notifications.setValue(showNotifications); rewriteSettings(); } public boolean isLinearMovement() { - return forceLinearPath; + return (Boolean)Option.linear.getValue(); } public void setLinearMovement(boolean linear) { - this.forceLinearPath = linear; + Option.linear.setValue(linear); rewriteSettings(); } public boolean isLightingEnabled() { - return lightingEnabled; + return (Boolean)Option.lighting.getValue(); + } + public void setUseResourcePacks(boolean use) { + Option.useResources.setValue(use); + rewriteSettings(); + } + public boolean getUseResourcePacks() { + return (Boolean)Option.useResources.getValue(); } + //TODO: FIX public void setLightingEnabled(boolean enabled) { - this.lightingEnabled = enabled; + Option.lighting.setValue(enabled); if(enabled) { Minecraft.getMinecraft().gameSettings.setOptionFloatValue(Options.GAMMA, 1000); } else { @@ -115,52 +129,28 @@ public class ReplaySettings { rewriteSettings(); } - - - public Map getOptions() { - Map map = new LinkedHashMap(); - - map.put("Enable Notifications", showNotifications); - map.put("Record Server", enableRecordingServer); - map.put("Record Singleplayer", enableRecordingSingleplayer); - map.put("Placeholder 1", null); - map.put("Force Linear Movement", forceLinearPath); - map.put("Enable Lighting", lightingEnabled); - map.put("Video Quality", videoQuality); - map.put("Video Framerate", videoFramerate); - - return map; - } - - public void setOptions(Map map) { - try { - - showNotifications = (Boolean)map.get("Enable Notifications"); - enableRecordingServer = (Boolean)map.get("Record Server"); - enableRecordingSingleplayer = (Boolean)map.get("Record Singleplayer"); - forceLinearPath = (Boolean)map.get("Force Linear Movement"); - lightingEnabled = (Boolean)map.get("Enable Lighting"); - videoQuality = (Float)map.get("Video Quality"); - videoFramerate = (Integer)map.get("Video Framerate"); - - rewriteSettings(); - } catch(Exception e) { - e.printStackTrace(); - } - } - + public void rewriteSettings() { ReplayMod.instance.config.load(); - ReplayMod.instance.config.removeCategory(ReplayMod.instance.config.getCategory("settings")); - Property recServer = ReplayMod.instance.config.get("settings", "enableRecordingServer", enableRecordingServer, "Defines whether a recording should be started upon joining a server."); - Property recSP = ReplayMod.instance.config.get("settings", "enableRecordingSingleplayer", enableRecordingSingleplayer, "Defines whether a recording should be started upon joining a singleplayer world."); - Property showNot = ReplayMod.instance.config.get("settings", "showNotifications", showNotifications, "Defines whether notifications should be sent to the player."); - Property linear = ReplayMod.instance.config.get("settings", "forceLinearPath", forceLinearPath, "Defines whether travelling paths should be linear instead of interpolated."); - Property lighting = ReplayMod.instance.config.get("settings", "enableLighting", lightingEnabled, "If enabled, the whole map is lighted."); - Property vq = ReplayMod.instance.config.get("settings", "videoQuality", videoQuality, "The quality of the exported video files from 0.1 to 0.9"); - Property framerate = ReplayMod.instance.config.get("settings", "videoFramerate", videoFramerate, "The framerate of the exported video files from 10 to 120"); + + for(Option o : Option.values()) { + addConfigSetting(ReplayMod.instance.config, o); + } ReplayMod.instance.config.save(); } + + private void addConfigSetting(Configuration config, Option o) { + Object value = o.getValue(); + if(value instanceof Integer) { + config.get("settings", o.name(), (Integer)o.getValue()); + } else if(value instanceof Boolean) { + config.get("settings", o.name(), (Boolean)o.getValue()); + } else if(value instanceof Double) { + config.get("settings", o.name(), (Double)o.getValue()); + } else if(value instanceof String) { + config.get("settings", o.name(), (String)o.getValue()); + } + } }