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
This commit is contained in:
Player 2014-04-01 21:56:53 +02:00
parent 3eaa002091
commit 407f6f79af
11 changed files with 228 additions and 147 deletions

View file

@ -10,7 +10,7 @@
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
@@ -107,14 +110,22 @@ @@ -107,20 +110,29 @@
NBTTagCompound nbttagcompound; NBTTagCompound nbttagcompound;
NBTTagCompound nbttagcompound1; NBTTagCompound nbttagcompound1;
@ -34,7 +34,14 @@
catch (Exception exception1) catch (Exception exception1)
{ {
exception1.printStackTrace(); 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)); nbttagcompound = CompressedStreamTools.func_74796_a(new FileInputStream(file1));
nbttagcompound1 = nbttagcompound.func_74775_l("Data"); nbttagcompound1 = nbttagcompound.func_74775_l("Data");
@ -50,7 +57,7 @@
catch (Exception exception) catch (Exception exception)
{ {
exception.printStackTrace(); exception.printStackTrace();
@@ -146,6 +163,8 @@ @@ -146,6 +164,8 @@
NBTTagCompound nbttagcompound2 = new NBTTagCompound(); NBTTagCompound nbttagcompound2 = new NBTTagCompound();
nbttagcompound2.func_74782_a("Data", nbttagcompound1); nbttagcompound2.func_74782_a("Data", nbttagcompound1);
@ -59,7 +66,7 @@
try try
{ {
File file1 = new File(this.field_75770_b, "level.dat_new"); File file1 = new File(this.field_75770_b, "level.dat_new");
@@ -184,6 +203,8 @@ @@ -184,6 +204,8 @@
NBTTagCompound nbttagcompound1 = new NBTTagCompound(); NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.func_74782_a("Data", nbttagcompound); nbttagcompound1.func_74782_a("Data", nbttagcompound);

View file

@ -21,9 +21,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -57,6 +55,7 @@ import net.minecraft.network.ServerStatusResponse;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldSettings;
import net.minecraft.world.storage.ISaveFormat;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.lwjgl.input.Mouse; 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.DummyModContainer;
import cpw.mods.fml.common.DuplicateModsFoundException; import cpw.mods.fml.common.DuplicateModsFoundException;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLContainer;
import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.IFMLSidedHandler; import cpw.mods.fml.common.IFMLSidedHandler;
import cpw.mods.fml.common.Loader; 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.ModMetadata;
import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.ObfuscationReflectionHelper;
import cpw.mods.fml.common.StartupQuery; import cpw.mods.fml.common.StartupQuery;
import cpw.mods.fml.common.WorldAccessContainer;
import cpw.mods.fml.common.WrongMinecraftVersionException; import cpw.mods.fml.common.WrongMinecraftVersionException;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLMissingMappingsEvent;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent.Action; import cpw.mods.fml.common.event.FMLMissingMappingsEvent.Action;
@ -441,6 +438,18 @@ public class FMLClientHandler implements IFMLSidedHandler
{ {
client.displayGuiScreen(new GuiConfirmation(query)); 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) public boolean handleLoadingScreen(ScaledResolution scaledResolution)
@ -486,6 +495,12 @@ public class FMLClientHandler implements IFMLSidedHandler
// NOOP // NOOP
} }
@Override
public ISaveFormat getSaveFormat()
{
return client.getSaveLoader();
}
@Override @Override
public MinecraftServer getServer() public MinecraftServer getServer()
{ {
@ -634,9 +649,16 @@ public class FMLClientHandler implements IFMLSidedHandler
showGuiScreen(new GuiOldSaveLoadConfirm(dirName, saveName, selectWorldGUI)); showGuiScreen(new GuiOldSaveLoadConfirm(dirName, saveName, selectWorldGUI));
} }
else else
{
try
{ {
client.launchIntegratedServer(dirName, saveName, (WorldSettings)null); client.launchIntegratedServer(dirName, saveName, (WorldSettings)null);
} }
catch (StartupQuery.AbortedException e)
{
// ignore
}
}
} }
public void showInGameModOptions(GuiIngameMenu guiIngameMenu) public void showInGameModOptions(GuiIngameMenu guiIngameMenu)
@ -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 @Override
public boolean shouldAllowPlayerLogins() public boolean shouldAllowPlayerLogins()
{ {

View file

@ -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<String> missingItems;
public GuiModItemsMissing(List<String> 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<String> 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_);
}
}

View file

@ -14,6 +14,7 @@ import org.apache.logging.log4j.Level;
import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.ObfuscationReflectionHelper;
import cpw.mods.fml.common.StartupQuery;
import cpw.mods.fml.common.ZipperUtil; import cpw.mods.fml.common.ZipperUtil;
public class GuiOldSaveLoadConfirm extends GuiYesNo { public class GuiOldSaveLoadConfirm extends GuiYesNo {
@ -69,7 +70,15 @@ public class GuiOldSaveLoadConfirm extends GuiYesNo {
return; return;
} }
FMLClientHandler.instance().showGuiScreen(null); FMLClientHandler.instance().showGuiScreen(null);
try
{
mc.launchIntegratedServer(dirName, saveName, (WorldSettings)null); mc.launchIntegratedServer(dirName, saveName, (WorldSettings)null);
} }
catch (StartupQuery.AbortedException e)
{
// ignore
}
}
} }
} }

View file

@ -15,8 +15,6 @@ package cpw.mods.fml.common;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
@ -30,6 +28,7 @@ import net.minecraft.network.INetHandler;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.storage.ISaveFormat;
import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.SaveHandler;
import net.minecraft.world.storage.WorldInfo; import net.minecraft.world.storage.WorldInfo;
@ -285,6 +284,10 @@ public class FMLCommonHandler
Loader.instance().serverStopping(); Loader.instance().serverStopping();
} }
public ISaveFormat getSaveFormat() {
return sidedDelegate.getSaveFormat();
}
public MinecraftServer getMinecraftServerInstance() public MinecraftServer getMinecraftServerInstance()
{ {
return sidedDelegate.getServer(); 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("<init>") &&
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() public boolean shouldServerBeKilledQuietly()
{ {
if (sidedDelegate == null) if (sidedDelegate == null)
@ -516,11 +546,6 @@ public class FMLCommonHandler
sidedDelegate.fireNetRegistrationEvent(bus(), manager, channelSet, channel, side); sidedDelegate.fireNetRegistrationEvent(bus(), manager, channelSet, channel, side);
} }
public FMLMissingMappingsEvent.Action getDefaultMissingAction()
{
return sidedDelegate.getDefaultMissingAction();
}
public boolean shouldAllowPlayerLogins() public boolean shouldAllowPlayerLogins()
{ {
return sidedDelegate.shouldAllowPlayerLogins(); return sidedDelegate.shouldAllowPlayerLogins();

View file

@ -219,7 +219,7 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
if (!tag.hasKey("BlockedItemIds")) // no blocked id info -> old 1.7 save 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... // fix the ids the best we can...
GameData.fixBrokenIds(dataList); GameData.fixBrokenIds(dataList);
} }
@ -248,9 +248,9 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
if (failedElements != null && !failedElements.isEmpty()) if (failedElements != null && !failedElements.isEmpty())
{ {
String text = "Forge Mod Loader could not load this save\n\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"+ "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"+ "You will not be able to load until they are present again.\n\n" +
"Missing Blocks/Items:\n"; "Missing Blocks/Items:\n";
for (String s : failedElements) text += s + "\n"; for (String s : failedElements) text += s + "\n";

View file

@ -14,12 +14,11 @@ package cpw.mods.fml.common;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import net.minecraft.network.INetHandler; import net.minecraft.network.INetHandler;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.world.storage.ISaveFormat;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLMissingMappingsEvent;
import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.eventhandler.EventBus;
import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.Side;
@ -40,6 +39,8 @@ public interface IFMLSidedHandler
void finishServerLoading(); void finishServerLoading();
ISaveFormat getSaveFormat();
MinecraftServer getServer(); MinecraftServer getServer();
boolean shouldServerShouldBeKilledQuietly(); boolean shouldServerShouldBeKilledQuietly();
@ -60,7 +61,5 @@ public interface IFMLSidedHandler
void fireNetRegistrationEvent(EventBus bus, NetworkManager manager, Set<String> channelSet, String channel, Side side); void fireNetRegistrationEvent(EventBus bus, NetworkManager manager, Set<String> channelSet, String channel, Side side);
FMLMissingMappingsEvent.Action getDefaultMissingAction();
boolean shouldAllowPlayerLogins(); boolean shouldAllowPlayerLogins();
} }

View file

@ -3,6 +3,8 @@ package cpw.mods.fml.common;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import net.minecraft.server.MinecraftServer;
public class StartupQuery { public class StartupQuery {
// internal class/functionality, do not use // internal class/functionality, do not use
@ -21,7 +23,9 @@ public class StartupQuery {
public static void abort() 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 aborted = true; // to abort loading and go back to the main menu
throw new AbortedException(); // to halt the server throw new AbortedException(); // to halt the server
} }
@ -78,6 +82,11 @@ public class StartupQuery {
return text; return text;
} }
public boolean isSynchronous()
{
return synchronous;
}
public void finish() public void finish()
{ {
signal.countDown(); signal.countDown();
@ -105,15 +114,23 @@ public class StartupQuery {
FMLLog.warning("Invalid value for fml.queryResult: %s, expected confirm or cancel", prop); FMLLog.warning("Invalid value for fml.queryResult: %s, expected confirm or cancel", prop);
} }
synchronous = false;
pending = this; // let the other thread start the query pending = this; // let the other thread start the query
// the client will eventually check pending and execute the query // from the integrated server thread: the client will eventually check pending and execute the query
// command handling in mc is synchronous, execute the server-side query directly // from the client thread: synchronous execution
if (FMLCommonHandler.instance().getSide().isServer()) check(); // 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 try
{ {
signal.await(); signal.await();
reset();
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
@ -125,6 +142,7 @@ public class StartupQuery {
private String text; private String text;
private AtomicBoolean result; private AtomicBoolean result;
private CountDownLatch signal = new CountDownLatch(1); private CountDownLatch signal = new CountDownLatch(1);
private volatile boolean synchronous;
/** /**

View file

@ -10,8 +10,13 @@ import java.util.Deque;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.logging.log4j.Level;
import com.google.common.io.Files; 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 * Copied from http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure
* because the code looked very tidy and neat. Thanks, McDowell! * because the code looked very tidy and neat. Thanks, McDowell!
@ -55,4 +60,39 @@ public class ZipperUtil {
res.close(); 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());
}
} }

View file

@ -41,11 +41,13 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.io.Files; import com.google.common.io.Files;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.StartupQuery; 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;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping; import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping;
import cpw.mods.fml.common.registry.GameRegistry.Type; import cpw.mods.fml.common.registry.GameRegistry.Type;
@ -243,6 +245,7 @@ public class GameData {
} }
Set<String> itemsToAllocate = new HashSet<String>(); Set<String> itemsToAllocate = new HashSet<String>();
Map<String, Integer> itemsToRelocate = new HashMap<String, Integer>();
// check all ids occupied by items // check all ids occupied by items
for (Entry<String, Integer> entry : dataList.entrySet()) for (Entry<String, Integer> entry : dataList.entrySet())
@ -259,18 +262,17 @@ public class GameData {
String blockName = '\u0001' + realName; String blockName = '\u0001' + realName;
if (!dataList.containsKey(blockName) || 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); 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 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 // relocate to the matching block
int newId = dataList.get(blockName); int newId = dataList.get(blockName);
entry.setValue(newId); itemsToRelocate.put(entry.getKey(), newId);
FMLLog.warning("Fixed ItemBlock %s not using the id of its block, old id %d, new id %d.", realName, oldId, newId);
} }
} }
else // unused id, occupy 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) for (String itemName : itemsToAllocate)
{ {
int oldId = dataList.get(itemName); 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); FMLLog.warning("Fixed Item %s conflicting with another block/item, old id %d, new id %d.", itemName.substring(1), oldId, newId);
} }
for (Map.Entry<String, Integer> 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<String> injectWorldIDMap(Map<String, Integer> dataList, boolean injectFrozenData, boolean isLocalWorld) public static List<String> injectWorldIDMap(Map<String, Integer> dataList, boolean injectFrozenData, boolean isLocalWorld)
@ -419,6 +452,7 @@ public class GameData {
List<String> failed = Lists.newArrayList(); List<String> failed = Lists.newArrayList();
List<String> ignored = Lists.newArrayList(); List<String> ignored = Lists.newArrayList();
List<String> warned = Lists.newArrayList(); List<String> warned = Lists.newArrayList();
List<String> defaulted = Lists.newArrayList();
for (MissingMapping remap : missedMappings) for (MissingMapping remap : missedMappings)
{ {
@ -462,10 +496,9 @@ public class GameData {
// block item missing, warn as requested and block the id // block item missing, warn as requested and block the id
if (action == FMLMissingMappingsEvent.Action.DEFAULT) if (action == FMLMissingMappingsEvent.Action.DEFAULT)
{ {
action = FMLCommonHandler.instance().getDefaultMissingAction(); defaulted.add(remap.name);
} }
else if (action == FMLMissingMappingsEvent.Action.IGNORE)
if (action == FMLMissingMappingsEvent.Action.IGNORE)
{ {
ignored.add(remap.name); ignored.add(remap.name);
} }
@ -477,14 +510,36 @@ public class GameData {
{ {
warned.add(remap.name); 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 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()) if (!failed.isEmpty())
{ {
FMLLog.severe("This world contains blocks and items that refuse to be remapped. The world will not be loaded"); 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); iItemRegistry.set(data.iItemRegistry);
availabilityMap.clear(); availabilityMap.clear();
availabilityMap.or(data.availabilityMap); availabilityMap.or(data.availabilityMap);
blockedIds.clear();
blockedIds.addAll(data.blockedIds); blockedIds.addAll(data.blockedIds);
} }

View file

@ -15,8 +15,6 @@ package cpw.mods.fml.server;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import net.minecraft.command.ServerCommand; import net.minecraft.command.ServerCommand;
import net.minecraft.network.INetHandler; import net.minecraft.network.INetHandler;
@ -24,6 +22,7 @@ import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.storage.ISaveFormat;
import com.google.common.collect.ImmutableList; 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.Loader;
import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.StartupQuery; import cpw.mods.fml.common.StartupQuery;
import cpw.mods.fml.common.WorldAccessContainer;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLMissingMappingsEvent;
import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.eventhandler.EventBus;
import cpw.mods.fml.common.network.FMLNetworkEvent; import cpw.mods.fml.common.network.FMLNetworkEvent;
@ -103,6 +101,12 @@ public class FMLServerHandler implements IFMLSidedHandler
throw new RuntimeException(message, exception); throw new RuntimeException(message, exception);
} }
@Override
public ISaveFormat getSaveFormat()
{
return server.getActiveAnvilConverter();
}
/** /**
* Get the server instance * 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."; "\nAlternatively start the server with -Dfml.queryResult=confirm or -Dfml.queryResult=cancel to preselect the answer.";
FMLLog.warning("%s", text); FMLLog.warning("%s", text);
if (!query.isSynchronous()) return; // no-op until mc does commands in another thread (if ever)
boolean done = false; boolean done = false;
while (!done && server.isServerRunning()) while (!done && server.isServerRunning())
@ -250,11 +256,7 @@ public class FMLServerHandler implements IFMLSidedHandler
{ {
bus.post(new FMLNetworkEvent.CustomPacketRegistrationEvent<NetHandlerPlayServer>(manager, channelSet, channel, side, NetHandlerPlayServer.class)); bus.post(new FMLNetworkEvent.CustomPacketRegistrationEvent<NetHandlerPlayServer>(manager, channelSet, channel, side, NetHandlerPlayServer.class));
} }
@Override
public FMLMissingMappingsEvent.Action getDefaultMissingAction()
{
return FMLMissingMappingsEvent.Action.valueOf(System.getProperty("fml.missingBlockAction", "FAIL"));
}
@Override @Override
public boolean shouldAllowPlayerLogins() public boolean shouldAllowPlayerLogins()
{ {