From 449ac98b77025eba38a75d0242113fffe26a8cf9 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 18 Jan 2014 22:25:35 -0500 Subject: [PATCH] SSP Worlds updating from 1.6 will now pop a warning message before loading, and will capture a timestamped zip file in the minecraft dir before starting to load. Allows for people to test updates. --- .../client/gui/GuiSelectWorld.java.patch | 18 +++++ .../fml/client/ExtendedServerListData.java | 8 +- .../cpw/mods/fml/client/FMLClientHandler.java | 65 +++++++++++++++-- .../cpw/mods/fml/client/GuiBackupFailed.java | 55 ++++++++++++++ .../fml/client/GuiOldSaveLoadConfirm.java | 73 +++++++++++++++++++ .../cpw/mods/fml/common/FMLCommonHandler.java | 2 +- .../java/cpw/mods/fml/common/ZipperUtil.java | 58 +++++++++++++++ 7 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.java.patch create mode 100644 src/main/java/cpw/mods/fml/client/GuiBackupFailed.java create mode 100644 src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java create mode 100644 src/main/java/cpw/mods/fml/common/ZipperUtil.java diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.java.patch new file mode 100644 index 0000000000..bd14fce513 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/net/minecraft/client/gui/GuiSelectWorld.java ++++ ../src-work/minecraft/net/minecraft/client/gui/GuiSelectWorld.java +@@ -1,5 +1,6 @@ + package net.minecraft.client.gui; + ++import cpw.mods.fml.client.FMLClientHandler; + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; + import java.text.DateFormat; +@@ -179,7 +180,7 @@ + + if (this.field_146297_k.func_71359_d().func_90033_f(s)) + { +- this.field_146297_k.func_71371_a(s, s1, (WorldSettings)null); ++ FMLClientHandler.instance().tryLoadExistingWorld(this, s, s1); + } + } + } diff --git a/src/main/java/cpw/mods/fml/client/ExtendedServerListData.java b/src/main/java/cpw/mods/fml/client/ExtendedServerListData.java index 1253e4c768..d204d4e6bc 100644 --- a/src/main/java/cpw/mods/fml/client/ExtendedServerListData.java +++ b/src/main/java/cpw/mods/fml/client/ExtendedServerListData.java @@ -1,16 +1,18 @@ package cpw.mods.fml.client; +import java.util.Map; + public class ExtendedServerListData { public final String type; public final boolean isCompatible; - public final int modCount; + public final Map modData; public final boolean isBlocked; - public ExtendedServerListData(String type, boolean isCompatible, int modCount, boolean isBlocked) + public ExtendedServerListData(String type, boolean isCompatible, Map modData, boolean isBlocked) { this.type = type; this.isCompatible = isCompatible; - this.modCount = modCount; + this.modData = modData; this.isBlocked = isBlocked; } } diff --git a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index dd9a4d317d..47ecc6ce65 100644 --- a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -12,6 +12,8 @@ */ package cpw.mods.fml.client; +import java.io.File; +import java.io.FileInputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -28,6 +30,7 @@ import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiIngameMenu; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSelectWorld; import net.minecraft.client.gui.ServerListEntryNormal; import net.minecraft.client.multiplayer.GuiConnecting; import net.minecraft.client.multiplayer.ServerData; @@ -42,6 +45,8 @@ import net.minecraft.crash.CrashReport; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.launchwrapper.Launch; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.INetHandler; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.NetworkManager; @@ -56,7 +61,10 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.Maps; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.common.DummyModContainer; @@ -559,6 +567,46 @@ public class FMLClientHandler implements IFMLSidedHandler playClientBlock = new CountDownLatch(1); } + public File getSavesDir() + { + return new File(client.field_71412_D, "saves"); + } + public void tryLoadExistingWorld(GuiSelectWorld selectWorldGUI, String dirName, String saveName) + { + File dir = new File(getSavesDir(), dirName); + NBTTagCompound leveldat; + try + { + leveldat = CompressedStreamTools.func_74796_a(new FileInputStream(new File(dir, "level.dat"))); + } + catch (Exception e) + { + try + { + leveldat = CompressedStreamTools.func_74796_a(new FileInputStream(new File(dir, "level.dat_old"))); + } + catch (Exception e1) + { + FMLLog.warning("There appears to be a problem loading the save %s, both level files are unreadable.", dirName); + return; + } + } + NBTTagCompound fmlData = leveldat.func_74775_l("FML"); + if (fmlData.func_74764_b("ModItemData")) + { + showGuiScreen(new GuiOldSaveLoadConfirm(dirName, saveName, selectWorldGUI)); + } + else + { + launchIntegratedServerCallback(dirName, saveName); + } + } + + public void launchIntegratedServerCallback(String dirName, String saveName) + { + client.func_71371_a(dirName, saveName, (WorldSettings)null); + } + public void showInGameModOptions(GuiIngameMenu guiIngameMenu) { showGuiScreen(new GuiIngameModOptions(guiIngameMenu)); @@ -590,9 +638,16 @@ public class FMLClientHandler implements IFMLSidedHandler { JsonObject jsonData = extraServerListData.get(originalResponse); String type = jsonData.get("type").getAsString(); - int modCount = jsonData.get("modList").getAsJsonArray().size(); + JsonArray modDataArray = jsonData.get("modList").getAsJsonArray(); boolean moddedClientAllowed = jsonData.has("clientModsAllowed") ? jsonData.get("clientModsAllowed").getAsBoolean() : true; - serverDataTag.put(data, new ExtendedServerListData(type, true, modCount, !moddedClientAllowed)); + Builder modListBldr = ImmutableMap.builder(); + for (JsonElement obj : modDataArray) + { + JsonObject modObj = obj.getAsJsonObject(); + modListBldr.put(modObj.get("modid").getAsString(), modObj.get("version").getAsString()); + } + + serverDataTag.put(data, new ExtendedServerListData(type, true, modListBldr.build(), !moddedClientAllowed)); } else { @@ -602,7 +657,7 @@ public class FMLClientHandler implements IFMLSidedHandler { moddedClientAllowed = !serverDescription.endsWith(":NOFML§r"); } - serverDataTag.put(data, new ExtendedServerListData("VANILLA", false, -1, !moddedClientAllowed)); + serverDataTag.put(data, new ExtendedServerListData("VANILLA", false, ImmutableMap.of(), !moddedClientAllowed)); } startupConnectionData.countDown(); } @@ -621,12 +676,12 @@ public class FMLClientHandler implements IFMLSidedHandler if ("FML".equals(extendedData.type) && extendedData.isCompatible) { idx = 0; - tooltip = String.format("Compatible FML modded server\n%d mods present", extendedData.modCount); + tooltip = String.format("Compatible FML modded server\n%d mods present", extendedData.modData.size()); } else if ("FML".equals(extendedData.type) && !extendedData.isCompatible) { idx = 16; - tooltip = String.format("Incompatible FML modded server\n%d mods present", extendedData.modCount); + tooltip = String.format("Incompatible FML modded server\n%d mods present", extendedData.modData.size()); } else if ("BUKKIT".equals(extendedData.type)) { diff --git a/src/main/java/cpw/mods/fml/client/GuiBackupFailed.java b/src/main/java/cpw/mods/fml/client/GuiBackupFailed.java new file mode 100644 index 0000000000..87bb221c7e --- /dev/null +++ b/src/main/java/cpw/mods/fml/client/GuiBackupFailed.java @@ -0,0 +1,55 @@ +/* + * Forge Mod Loader + * Copyright (c) 2012-2013 cpw. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser Public License v2.1 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Contributors: + * cpw - implementation + */ + +package cpw.mods.fml.client; + +import java.io.File; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +public class GuiBackupFailed extends GuiScreen +{ + private GuiScreen parent; + private File zipName; + public GuiBackupFailed(GuiScreen parent, File zipName) + { + this.parent = parent; + this.zipName = zipName; + } + + @SuppressWarnings("unchecked") + @Override + public void func_73866_w_() + { + this.field_146292_n.add(new GuiButton(1, this.field_146294_l / 2 - 75, this.field_146295_m - 38, I18n.func_135052_a("gui.done"))); + } + + @Override + protected void func_146284_a(GuiButton p_73875_1_) + { + if (p_73875_1_.field_146124_l && p_73875_1_.field_146127_k == 1) + { + FMLClientHandler.instance().showGuiScreen(parent); + } + } + @Override + public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) + { + this.func_146276_q_(); + int offset = Math.max(85 - 2 * 10, 10); + this.func_73732_a(this.field_146289_q, String.format("There was an error saving the archive %s", zipName.getName()), this.field_146294_l / 2, offset, 0xFFFFFF); + offset += 10; + this.func_73732_a(this.field_146289_q, String.format("Please fix the problem and try again"), this.field_146294_l / 2, offset, 0xFFFFFF); + super.func_73863_a(p_73863_1_, p_73863_2_, p_73863_3_); + } +} diff --git a/src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java b/src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java new file mode 100644 index 0000000000..7bb9d0a5ff --- /dev/null +++ b/src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java @@ -0,0 +1,73 @@ +package cpw.mods.fml.client; + +import java.io.File; +import java.io.IOException; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiLabel; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSelectWorld; +import net.minecraft.client.gui.GuiYesNo; +import org.apache.logging.log4j.Level; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.ObfuscationReflectionHelper; +import cpw.mods.fml.common.ZipperUtil; + +public class GuiOldSaveLoadConfirm extends GuiYesNo { + + private String dirName; + private String saveName; + private File zip; + public GuiOldSaveLoadConfirm(String dirName, String saveName, GuiScreen parent) + { + super(parent, "", "", 0); + this.dirName = dirName; + this.saveName = saveName; + this.zip = new File(FMLClientHandler.instance().getClient().field_71412_D,String.format("%s-%2$td%2$tm%2$ty%2$tH%2$tM%2$tS.zip", saveName, System.currentTimeMillis())); + } + @Override + public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) + { + this.func_146276_q_(); + this.func_73732_a(this.field_146289_q, String.format("The world %s contains pre-update modding data", saveName), this.field_146294_l / 2, 50, 16777215); + this.func_73732_a(this.field_146289_q, String.format("There may be problems updating it to this version"), this.field_146294_l / 2, 70, 16777215); + this.func_73732_a(this.field_146289_q, String.format("FML will save a zip to %s", zip.getName()), this.field_146294_l / 2, 90, 16777215); + this.func_73732_a(this.field_146289_q, String.format("Do you wish to continue loading?"), this.field_146294_l / 2, 110, 16777215); + int k; + + for (k = 0; k < this.field_146292_n.size(); ++k) + { + ((GuiButton)this.field_146292_n.get(k)).func_146112_a(this.field_146297_k, p_73863_1_, p_73863_2_); + } + + for (k = 0; k < this.field_146293_o.size(); ++k) + { + ((GuiLabel)this.field_146293_o.get(k)).func_146159_a(this.field_146297_k, p_73863_1_, p_73863_2_); + } + } + @Override + protected void func_146284_a(GuiButton p_146284_1_) + { + if (p_146284_1_.field_146127_k == 1) + { + ObfuscationReflectionHelper.setPrivateValue(GuiSelectWorld.class, (GuiSelectWorld)field_146355_a, false, "field_"+"146634_i"); + FMLClientHandler.instance().showGuiScreen(field_146355_a); + } + else + { + FMLLog.info("Capturing current state of world %s into file %s", saveName, zip.getAbsolutePath()); + try + { + ZipperUtil.zip(new File(FMLClientHandler.instance().getSavesDir(), dirName), zip); + } catch (IOException e) + { + FMLLog.log(Level.WARN, e, "There was a problem saving the backup %s. Please fix and try again", zip.getName()); + FMLClientHandler.instance().showGuiScreen(new GuiBackupFailed(field_146355_a, zip)); + return; + } + FMLClientHandler.instance().showGuiScreen(null); + FMLClientHandler.instance().launchIntegratedServerCallback(dirName, saveName); + } + + } + +} diff --git a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java index 6418f1a95f..f7617c285e 100644 --- a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java +++ b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java @@ -371,7 +371,7 @@ public class FMLCommonHandler } handlerSet.add(handler); Map additionalProperties = Maps.newHashMap(); -// worldInfo.setAdditionalProperties(additionalProperties); + worldInfo.setAdditionalProperties(additionalProperties); for (ModContainer mc : Loader.instance().getModList()) { if (mc instanceof InjectedModContainer) diff --git a/src/main/java/cpw/mods/fml/common/ZipperUtil.java b/src/main/java/cpw/mods/fml/common/ZipperUtil.java new file mode 100644 index 0000000000..79466db3c4 --- /dev/null +++ b/src/main/java/cpw/mods/fml/common/ZipperUtil.java @@ -0,0 +1,58 @@ +package cpw.mods.fml.common; + +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.Deque; +import java.util.LinkedList; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import com.google.common.io.Files; + +/** + * Copied from http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure + * because the code looked very tidy and neat. Thanks, McDowell! + * + * @author McDowell + * + */ +public class ZipperUtil { + public static void zip(File directory, File zipfile) throws IOException + { + URI base = directory.toURI(); + Deque queue = new LinkedList(); + queue.push(directory); + OutputStream out = new FileOutputStream(zipfile); + Closeable res = null; + try + { + ZipOutputStream zout = new ZipOutputStream(out); + res = zout; + while (!queue.isEmpty()) + { + directory = queue.pop(); + for (File kid : directory.listFiles()) + { + String name = base.relativize(kid.toURI()).getPath(); + if (kid.isDirectory()) + { + queue.push(kid); + name = name.endsWith("/") ? name : name + "/"; + zout.putNextEntry(new ZipEntry(name)); + } else + { + zout.putNextEntry(new ZipEntry(name)); + Files.copy(kid, zout); + zout.closeEntry(); + } + } + } + } finally + { + res.close(); + } + } +}