From fd6d55afcc4f4c650c143ad43e09fbdc2cb9d850 Mon Sep 17 00:00:00 2001 From: Player Date: Tue, 1 Apr 2014 21:56:53 +0200 Subject: [PATCH] Registry: Allow ignoring missing mods from the GUI, with confirm+backup Registry: Add confirm+backup for automated corrupted id table fixup Require the user to confirm loading from a backup level.dat --- .../world/storage/SaveHandler.java.patch | 15 +++- .../cpw/mods/fml/client/FMLClientHandler.java | 45 ++++++---- .../mods/fml/client/GuiModItemsMissing.java | 84 ------------------- .../fml/client/GuiOldSaveLoadConfirm.java | 11 ++- .../cpw/mods/fml/common/FMLCommonHandler.java | 39 +++++++-- .../cpw/mods/fml/common/FMLContainer.java | 10 +-- .../cpw/mods/fml/common/IFMLSidedHandler.java | 7 +- .../cpw/mods/fml/common/StartupQuery.java | 26 +++++- .../java/cpw/mods/fml/common/ZipperUtil.java | 40 +++++++++ .../mods/fml/common/registry/GameData.java | 80 +++++++++++++++--- .../cpw/mods/fml/server/FMLServerHandler.java | 18 ++-- 11 files changed, 228 insertions(+), 147 deletions(-) delete mode 100644 src/main/java/cpw/mods/fml/client/GuiModItemsMissing.java diff --git a/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch b/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch index 11c0322320..027bea2c53 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch +++ b/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch @@ -10,7 +10,7 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; -@@ -107,14 +110,22 @@ +@@ -107,20 +110,29 @@ NBTTagCompound nbttagcompound; NBTTagCompound nbttagcompound1; @@ -34,7 +34,14 @@ catch (Exception exception1) { exception1.printStackTrace(); -@@ -129,8 +140,14 @@ + } + } + ++ FMLCommonHandler.instance().confirmBackupLevelDatUse(); + file1 = new File(this.field_75770_b, "level.dat_old"); + + if (file1.exists()) +@@ -129,8 +141,14 @@ { nbttagcompound = CompressedStreamTools.func_74796_a(new FileInputStream(file1)); nbttagcompound1 = nbttagcompound.func_74775_l("Data"); @@ -50,7 +57,7 @@ catch (Exception exception) { exception.printStackTrace(); -@@ -146,6 +163,8 @@ +@@ -146,6 +164,8 @@ NBTTagCompound nbttagcompound2 = new NBTTagCompound(); nbttagcompound2.func_74782_a("Data", nbttagcompound1); @@ -59,7 +66,7 @@ try { File file1 = new File(this.field_75770_b, "level.dat_new"); -@@ -184,6 +203,8 @@ +@@ -184,6 +204,8 @@ NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.func_74782_a("Data", nbttagcompound); diff --git a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index c8265df13c..56f894b04d 100644 --- a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -21,9 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import net.minecraft.client.Minecraft; @@ -57,6 +55,7 @@ import net.minecraft.network.ServerStatusResponse; import net.minecraft.server.MinecraftServer; import net.minecraft.util.ResourceLocation; import net.minecraft.world.WorldSettings; +import net.minecraft.world.storage.ISaveFormat; import org.apache.logging.log4j.Level; import org.lwjgl.input.Mouse; @@ -77,7 +76,6 @@ import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.common.DummyModContainer; import cpw.mods.fml.common.DuplicateModsFoundException; import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.common.FMLContainer; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.IFMLSidedHandler; import cpw.mods.fml.common.Loader; @@ -88,7 +86,6 @@ import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.StartupQuery; -import cpw.mods.fml.common.WorldAccessContainer; import cpw.mods.fml.common.WrongMinecraftVersionException; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLMissingMappingsEvent.Action; @@ -441,6 +438,18 @@ public class FMLClientHandler implements IFMLSidedHandler { client.displayGuiScreen(new GuiConfirmation(query)); } + + if (query.isSynchronous()) + { + while (!(client.currentScreen instanceof GuiMainMenu)) + { + if (Thread.interrupted()) throw new InterruptedException(); + + client.loadingScreen.resetProgresAndWorkingMessage(""); + + Thread.sleep(50); + } + } } public boolean handleLoadingScreen(ScaledResolution scaledResolution) @@ -486,6 +495,12 @@ public class FMLClientHandler implements IFMLSidedHandler // NOOP } + @Override + public ISaveFormat getSaveFormat() + { + return client.getSaveLoader(); + } + @Override public MinecraftServer getServer() { @@ -635,7 +650,14 @@ public class FMLClientHandler implements IFMLSidedHandler } else { - client.launchIntegratedServer(dirName, saveName, (WorldSettings)null); + try + { + client.launchIntegratedServer(dirName, saveName, (WorldSettings)null); + } + catch (StartupQuery.AbortedException e) + { + // ignore + } } } @@ -820,19 +842,6 @@ public class FMLClientHandler implements IFMLSidedHandler } } - public void setDefaultMissingAction(FMLMissingMappingsEvent.Action action) - { - this.defaultMissingAction = action; - } - - private Action defaultMissingAction = FMLMissingMappingsEvent.Action.FAIL; - - @Override - public Action getDefaultMissingAction() - { - return defaultMissingAction; - } - @Override public boolean shouldAllowPlayerLogins() { diff --git a/src/main/java/cpw/mods/fml/client/GuiModItemsMissing.java b/src/main/java/cpw/mods/fml/client/GuiModItemsMissing.java deleted file mode 100644 index e21888c492..0000000000 --- a/src/main/java/cpw/mods/fml/client/GuiModItemsMissing.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.util.Iterator; -import java.util.List; - -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; - -public class GuiModItemsMissing extends GuiScreen -{ - private List missingItems; - - public GuiModItemsMissing(List items) - { - this.missingItems = items; - } - - @SuppressWarnings("unchecked") - @Override - public void initGui() - { - this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height - 38, I18n.format("gui.done"))); - } - - @Override - protected void actionPerformed(GuiButton p_73875_1_) - { - if (p_73875_1_.enabled && p_73875_1_.id == 1) - { - FMLClientHandler.instance().showGuiScreen(null); - } - } - @Override - public void drawScreen(int p_73863_1_, int p_73863_2_, float p_73863_3_) - { - this.drawDefaultBackground(); - - int spaceAvailable = this.height - 38 - 20; - int spaceRequired = Math.min(spaceAvailable, 10 + 6 * 10 + missingItems.size()); - - int offset = 10 + (spaceAvailable - spaceRequired) / 2; // vertically centered - this.drawCenteredString(this.fontRendererObj, "Forge Mod Loader could load this save", this.width / 2, offset, 0xFFFFFF); - offset += 20; - this.drawCenteredString(this.fontRendererObj, String.format("There are %d unassigned blocks and items in this save", missingItems.size()), this.width / 2, offset, 0xFFFFFF); - offset += 10; - this.drawCenteredString(this.fontRendererObj, "You will not be able to load until they are present again", this.width / 2, offset, 0xFFFFFF); - offset += 20; - - this.drawCenteredString(this.fontRendererObj, "Missing Blocks/Items:", this.width / 2, offset, 0xFFFFFF); - offset += 10; - - Iterator it = missingItems.iterator(); - - while (it.hasNext()) - { - String item = it.next(); - - this.drawCenteredString(this.fontRendererObj, item, this.width / 2, offset, 0xFFFFFF); - offset += 10; - - if (offset >= spaceAvailable) break; - } - - if (it.hasNext()) - { - this.drawCenteredString(this.fontRendererObj, "...", this.width / 2, offset, 0xFFFFFF); - } - - super.drawScreen(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 index 3b0d900f75..426daa06bf 100644 --- a/src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java +++ b/src/main/java/cpw/mods/fml/client/GuiOldSaveLoadConfirm.java @@ -14,6 +14,7 @@ import org.apache.logging.log4j.Level; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.ObfuscationReflectionHelper; +import cpw.mods.fml.common.StartupQuery; import cpw.mods.fml.common.ZipperUtil; public class GuiOldSaveLoadConfirm extends GuiYesNo { @@ -69,7 +70,15 @@ public class GuiOldSaveLoadConfirm extends GuiYesNo { return; } FMLClientHandler.instance().showGuiScreen(null); - mc.launchIntegratedServer(dirName, saveName, (WorldSettings)null); + + try + { + mc.launchIntegratedServer(dirName, saveName, (WorldSettings)null); + } + catch (StartupQuery.AbortedException e) + { + // ignore + } } } } diff --git a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java index 5573c0d6cc..3d81d2201b 100644 --- a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java +++ b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java @@ -15,8 +15,6 @@ package cpw.mods.fml.common; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; @@ -30,6 +28,7 @@ import net.minecraft.network.INetHandler; import net.minecraft.network.NetworkManager; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; +import net.minecraft.world.storage.ISaveFormat; import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; @@ -285,6 +284,10 @@ public class FMLCommonHandler Loader.instance().serverStopping(); } + public ISaveFormat getSaveFormat() { + return sidedDelegate.getSaveFormat(); + } + public MinecraftServer getMinecraftServerInstance() { return sidedDelegate.getServer(); @@ -397,6 +400,33 @@ public class FMLCommonHandler } } + public void confirmBackupLevelDatUse() + { + // ignore invocations from the world ctor, those are always preceded by another invocation + for (StackTraceElement e : Thread.currentThread().getStackTrace()) { + try + { + if (e.getMethodName().equals("") && + Class.forName(e.getClassName()) == World.class) + { + return; + } + } + catch (ClassNotFoundException e1) + { + // nothing + } + } + + String text = "Forge Mod Loader detected that the backup level.dat is being used.\n\n" + + "This may happen due to a bug or corruption, continuing can damage\n" + + "your world beyond repair or lose data / progress.\n\n" + + "It's recommended to create a world backup before continuing."; + + boolean confirmed = StartupQuery.confirm(text); + if (!confirmed) StartupQuery.abort(); + } + public boolean shouldServerBeKilledQuietly() { if (sidedDelegate == null) @@ -516,11 +546,6 @@ public class FMLCommonHandler sidedDelegate.fireNetRegistrationEvent(bus(), manager, channelSet, channel, side); } - public FMLMissingMappingsEvent.Action getDefaultMissingAction() - { - return sidedDelegate.getDefaultMissingAction(); - } - public boolean shouldAllowPlayerLogins() { return sidedDelegate.shouldAllowPlayerLogins(); diff --git a/src/main/java/cpw/mods/fml/common/FMLContainer.java b/src/main/java/cpw/mods/fml/common/FMLContainer.java index 8e699e26ce..8aecde9198 100644 --- a/src/main/java/cpw/mods/fml/common/FMLContainer.java +++ b/src/main/java/cpw/mods/fml/common/FMLContainer.java @@ -219,7 +219,7 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai if (!tag.hasKey("BlockedItemIds")) // no blocked id info -> old 1.7 save { - // old 1.7 save potentially affected by the registry mapping bug + // old early 1.7 save potentially affected by the registry mapping bug // fix the ids the best we can... GameData.fixBrokenIds(dataList); } @@ -248,10 +248,10 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai if (failedElements != null && !failedElements.isEmpty()) { - String text = "Forge Mod Loader could not load this save\n\n" + - "There are "+failedElements.size()+" unassigned blocks and items in this save\n"+ - "You will not be able to load until they are present again\n\n"+ - "Missing Blocks/Items:\n"; + String text = "Forge Mod Loader could not load this save.\n\n" + + "There are "+failedElements.size()+" unassigned blocks and items in this save.\n" + + "You will not be able to load until they are present again.\n\n" + + "Missing Blocks/Items:\n"; for (String s : failedElements) text += s + "\n"; diff --git a/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java b/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java index c11ac23063..2000a8df14 100644 --- a/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java +++ b/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java @@ -14,12 +14,11 @@ package cpw.mods.fml.common; import java.util.List; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import net.minecraft.network.INetHandler; import net.minecraft.network.NetworkManager; import net.minecraft.server.MinecraftServer; +import net.minecraft.world.storage.ISaveFormat; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.relauncher.Side; @@ -40,6 +39,8 @@ public interface IFMLSidedHandler void finishServerLoading(); + ISaveFormat getSaveFormat(); + MinecraftServer getServer(); boolean shouldServerShouldBeKilledQuietly(); @@ -60,7 +61,5 @@ public interface IFMLSidedHandler void fireNetRegistrationEvent(EventBus bus, NetworkManager manager, Set channelSet, String channel, Side side); - FMLMissingMappingsEvent.Action getDefaultMissingAction(); - boolean shouldAllowPlayerLogins(); } diff --git a/src/main/java/cpw/mods/fml/common/StartupQuery.java b/src/main/java/cpw/mods/fml/common/StartupQuery.java index 64668e6bca..07d138983c 100644 --- a/src/main/java/cpw/mods/fml/common/StartupQuery.java +++ b/src/main/java/cpw/mods/fml/common/StartupQuery.java @@ -3,6 +3,8 @@ package cpw.mods.fml.common; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; +import net.minecraft.server.MinecraftServer; + public class StartupQuery { // internal class/functionality, do not use @@ -21,7 +23,9 @@ public class StartupQuery { public static void abort() { - FMLCommonHandler.instance().getMinecraftServerInstance().initiateShutdown(); + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + if (server != null) server.initiateShutdown(); + aborted = true; // to abort loading and go back to the main menu throw new AbortedException(); // to halt the server } @@ -78,6 +82,11 @@ public class StartupQuery { return text; } + public boolean isSynchronous() + { + return synchronous; + } + public void finish() { signal.countDown(); @@ -105,15 +114,23 @@ public class StartupQuery { FMLLog.warning("Invalid value for fml.queryResult: %s, expected confirm or cancel", prop); } + synchronous = false; pending = this; // let the other thread start the query - // the client will eventually check pending and execute the query - // command handling in mc is synchronous, execute the server-side query directly - if (FMLCommonHandler.instance().getSide().isServer()) check(); + // from the integrated server thread: the client will eventually check pending and execute the query + // from the client thread: synchronous execution + // dedicated server: command handling in mc is synchronous, execute the server-side query directly + if (FMLCommonHandler.instance().getSide().isServer() || + FMLCommonHandler.instance().getEffectiveSide().isClient()) + { + synchronous = true; + check(); + } try { signal.await(); + reset(); } catch (InterruptedException e) { @@ -125,6 +142,7 @@ public class StartupQuery { private String text; private AtomicBoolean result; private CountDownLatch signal = new CountDownLatch(1); + private volatile boolean synchronous; /** diff --git a/src/main/java/cpw/mods/fml/common/ZipperUtil.java b/src/main/java/cpw/mods/fml/common/ZipperUtil.java index 79466db3c4..c97297a44e 100644 --- a/src/main/java/cpw/mods/fml/common/ZipperUtil.java +++ b/src/main/java/cpw/mods/fml/common/ZipperUtil.java @@ -10,8 +10,13 @@ import java.util.Deque; import java.util.LinkedList; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; + +import org.apache.logging.log4j.Level; + import com.google.common.io.Files; +import cpw.mods.fml.client.FMLClientHandler; + /** * Copied from http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure * because the code looked very tidy and neat. Thanks, McDowell! @@ -55,4 +60,39 @@ public class ZipperUtil { res.close(); } } + + public static void backupWorld() throws IOException + { + String dirName = FMLCommonHandler.instance().getMinecraftServerInstance().getFolderName(); + String saveName; + + if (FMLCommonHandler.instance().getSide().isClient()) + { + saveName = FMLCommonHandler.instance().getMinecraftServerInstance().getWorldName(); + } + else + { + saveName = dirName; + } + + backupWorld(dirName, saveName); + } + + public static void backupWorld(String dirName, String saveName) throws IOException + { + File dstFolder = FMLCommonHandler.instance().getSaveFormat().getSaveLoader(dirName, false).getWorldDirectory().getParentFile(); + File zip = new File(dstFolder, String.format("%s-%2$tY%2$tm%2$td-%2$tH%2$tM%2$tS.zip", saveName, System.currentTimeMillis())); + + try + { + ZipperUtil.zip(new File(dstFolder, dirName), zip); + } + catch (IOException e) + { + FMLLog.log(Level.WARN, e, "World backup failed."); + throw e; + } + + FMLLog.info("World backup created at %s.", zip.getCanonicalPath()); + } } diff --git a/src/main/java/cpw/mods/fml/common/registry/GameData.java b/src/main/java/cpw/mods/fml/common/registry/GameData.java index 2a15292443..da9871c151 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -41,11 +41,13 @@ import com.google.common.collect.Maps; import com.google.common.collect.Table; import com.google.common.io.Files; +import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.StartupQuery; +import cpw.mods.fml.common.ZipperUtil; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping; import cpw.mods.fml.common.registry.GameRegistry.Type; @@ -243,6 +245,7 @@ public class GameData { } Set itemsToAllocate = new HashSet(); + Map itemsToRelocate = new HashMap(); // check all ids occupied by items for (Entry entry : dataList.entrySet()) @@ -259,18 +262,17 @@ public class GameData { String blockName = '\u0001' + realName; if (!dataList.containsKey(blockName) || - !(getMain().iItemRegistry.getRaw(realName) instanceof ItemBlock)) // the slot is occupied by something else and this item is no ItemBlock + (getMain().iItemRegistry.getRaw(realName) != null && // don't assume missing items are no ItemBlock + !(getMain().iItemRegistry.getRaw(realName) instanceof ItemBlock))) // the slot is occupied by something else and this item is no ItemBlock { - // relocate the item later, after all correct ids have been claimed + // allocate the item later, after all correct ids have been claimed itemsToAllocate.add(itemName); } else if (dataList.get(blockName) != oldId) // occupied, but this is an ItemBlock for a different block than whatever may use its id { // relocate to the matching block int newId = dataList.get(blockName); - entry.setValue(newId); - - FMLLog.warning("Fixed ItemBlock %s not using the id of its block, old id %d, new id %d.", realName, oldId, newId); + itemsToRelocate.put(entry.getKey(), newId); } } else // unused id, occupy @@ -280,6 +282,27 @@ public class GameData { } } + if (itemsToAllocate.isEmpty() && itemsToRelocate.isEmpty()) return; // nothing to do + + String text = "Forge Mod Loader detected that this save is damaged.\n\n" + + "It's likely that an automatic repair can successfully restore\n" + + "most of it, except some items which may get swapped with others.\n\n" + + "A world backup will be created as a zip file in your saves\n"+ + "directory automatically."; + + boolean confirmed = StartupQuery.confirm(text); + if (!confirmed) StartupQuery.abort(); + + try + { + ZipperUtil.backupWorld(); + } + catch (IOException e) + { + StartupQuery.notify("The world backup couldn't be created.\n\n"+e); + StartupQuery.abort(); + } + for (String itemName : itemsToAllocate) { int oldId = dataList.get(itemName); @@ -289,6 +312,16 @@ public class GameData { FMLLog.warning("Fixed Item %s conflicting with another block/item, old id %d, new id %d.", itemName.substring(1), oldId, newId); } + + for (Map.Entry entry : itemsToRelocate.entrySet()) + { + String itemName = entry.getKey(); + int newId = entry.getValue(); + + int oldId = dataList.put(itemName, newId); + + FMLLog.warning("Fixed ItemBlock %s not using the id of its block, old id %d, new id %d.", itemName.substring(1), oldId, newId); + } } public static List injectWorldIDMap(Map dataList, boolean injectFrozenData, boolean isLocalWorld) @@ -419,6 +452,7 @@ public class GameData { List failed = Lists.newArrayList(); List ignored = Lists.newArrayList(); List warned = Lists.newArrayList(); + List defaulted = Lists.newArrayList(); for (MissingMapping remap : missedMappings) { @@ -462,10 +496,9 @@ public class GameData { // block item missing, warn as requested and block the id if (action == FMLMissingMappingsEvent.Action.DEFAULT) { - action = FMLCommonHandler.instance().getDefaultMissingAction(); + defaulted.add(remap.name); } - - if (action == FMLMissingMappingsEvent.Action.IGNORE) + else if (action == FMLMissingMappingsEvent.Action.IGNORE) { ignored.add(remap.name); } @@ -477,14 +510,36 @@ public class GameData { { warned.add(remap.name); } - else - { - throw new RuntimeException(String.format("Invalid default missing id action specified: %s", action.name())); - } gameData.block(remap.id); // prevent the id from being reused later } } + + if (!defaulted.isEmpty()) + { + String text = "Forge Mod Loader detected missing blocks/items.\n\n" + + "There are "+defaulted.size()+" missing blocks and items in this save.\n" + + "If you continue the missing blocks/items will get removed.\n" + + "A world backup will be automatically created in your saves directory.\n\n" + + "Missing Blocks/Items:\n"; + + for (String s : defaulted) text += s + "\n"; + + boolean confirmed = StartupQuery.confirm(text); + if (!confirmed) StartupQuery.abort(); + + try + { + ZipperUtil.backupWorld(); + } + catch (IOException e) + { + StartupQuery.notify("The world backup couldn't be created.\n\n"+e); + StartupQuery.abort(); + } + + warned.addAll(defaulted); + } if (!failed.isEmpty()) { FMLLog.severe("This world contains blocks and items that refuse to be remapped. The world will not be loaded"); @@ -557,6 +612,7 @@ public class GameData { iItemRegistry.set(data.iItemRegistry); availabilityMap.clear(); availabilityMap.or(data.availabilityMap); + blockedIds.clear(); blockedIds.addAll(data.blockedIds); } diff --git a/src/main/java/cpw/mods/fml/server/FMLServerHandler.java b/src/main/java/cpw/mods/fml/server/FMLServerHandler.java index 7b72c34c58..6546c5a1e8 100644 --- a/src/main/java/cpw/mods/fml/server/FMLServerHandler.java +++ b/src/main/java/cpw/mods/fml/server/FMLServerHandler.java @@ -15,8 +15,6 @@ package cpw.mods.fml.server; import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import net.minecraft.command.ServerCommand; import net.minecraft.network.INetHandler; @@ -24,6 +22,7 @@ import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.NetworkManager; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.world.storage.ISaveFormat; import com.google.common.collect.ImmutableList; @@ -33,7 +32,6 @@ import cpw.mods.fml.common.IFMLSidedHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.StartupQuery; -import cpw.mods.fml.common.WorldAccessContainer; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.network.FMLNetworkEvent; @@ -103,6 +101,12 @@ public class FMLServerHandler implements IFMLSidedHandler throw new RuntimeException(message, exception); } + @Override + public ISaveFormat getSaveFormat() + { + return server.getActiveAnvilConverter(); + } + /** * Get the server instance */ @@ -159,6 +163,8 @@ public class FMLServerHandler implements IFMLSidedHandler "\nAlternatively start the server with -Dfml.queryResult=confirm or -Dfml.queryResult=cancel to preselect the answer."; FMLLog.warning("%s", text); + if (!query.isSynchronous()) return; // no-op until mc does commands in another thread (if ever) + boolean done = false; while (!done && server.isServerRunning()) @@ -250,11 +256,7 @@ public class FMLServerHandler implements IFMLSidedHandler { bus.post(new FMLNetworkEvent.CustomPacketRegistrationEvent(manager, channelSet, channel, side, NetHandlerPlayServer.class)); } - @Override - public FMLMissingMappingsEvent.Action getDefaultMissingAction() - { - return FMLMissingMappingsEvent.Action.valueOf(System.getProperty("fml.missingBlockAction", "FAIL")); - } + @Override public boolean shouldAllowPlayerLogins() {