From 7284104472e5f1017f74bbd37fef6e25a98e3b93 Mon Sep 17 00:00:00 2001 From: Player Date: Sat, 5 Apr 2014 01:47:19 +0200 Subject: [PATCH] Registry: Repair mismatched ItemBlocks as well Fix a few misc issues --- .../world/storage/SaveHandler.java.patch | 2 +- .../cpw/mods/fml/client/FMLClientHandler.java | 8 +- .../cpw/mods/fml/common/FMLCommonHandler.java | 30 ++---- .../cpw/mods/fml/common/FMLContainer.java | 11 +- .../cpw/mods/fml/common/IFMLSidedHandler.java | 5 +- .../cpw/mods/fml/common/LoadController.java | 2 +- .../java/cpw/mods/fml/common/ZipperUtil.java | 2 +- .../mods/fml/common/registry/GameData.java | 101 ++++++++++++------ .../cpw/mods/fml/server/FMLServerHandler.java | 8 +- fml/src/main/resources/fml_at.cfg | 2 + 10 files changed, 105 insertions(+), 66 deletions(-) diff --git a/fml/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch b/fml/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch index 027bea2c5..dddda84c7 100644 --- a/fml/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch +++ b/fml/patches/minecraft/net/minecraft/world/storage/SaveHandler.java.patch @@ -37,7 +37,7 @@ } } -+ FMLCommonHandler.instance().confirmBackupLevelDatUse(); ++ FMLCommonHandler.instance().confirmBackupLevelDatUse(this); file1 = new File(this.field_75770_b, "level.dat_old"); if (file1.exists()) diff --git a/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index fe3c92441..bbdacca5f 100644 --- a/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -55,7 +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 net.minecraft.world.storage.SaveFormatOld; import org.apache.logging.log4j.Level; import org.lwjgl.input.Mouse; @@ -87,8 +87,6 @@ import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.StartupQuery; import cpw.mods.fml.common.WrongMinecraftVersionException; -import cpw.mods.fml.common.event.FMLMissingMappingsEvent; -import cpw.mods.fml.common.event.FMLMissingMappingsEvent.Action; import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.network.FMLNetworkEvent; import cpw.mods.fml.common.registry.GameData; @@ -496,9 +494,9 @@ public class FMLClientHandler implements IFMLSidedHandler } @Override - public ISaveFormat getSaveFormat() + public File getSavesDirectory() { - return client.getSaveLoader(); + return ((SaveFormatOld) client.getSaveLoader()).savesDirectory; } @Override diff --git a/fml/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java b/fml/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java index 3d81d2201..96fc9a07a 100644 --- a/fml/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java +++ b/fml/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java @@ -12,6 +12,8 @@ package cpw.mods.fml.common; +import java.io.File; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; import java.util.Set; @@ -28,7 +30,6 @@ 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; @@ -44,7 +45,6 @@ import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.gameevent.InputEvent; import cpw.mods.fml.common.gameevent.PlayerEvent; @@ -84,6 +84,7 @@ public class FMLCommonHandler private List brandingsNoMC; private List crashCallables = Lists.newArrayList(Loader.instance().getCallableCrashInformation()); private Set handlerSet = Sets.newSetFromMap(new MapMaker().weakKeys().makeMap()); + private WeakReference handlerToCheck; private EventBus eventBus = new EventBus(); /** * The FML event bus. Subscribe here for FML related events @@ -284,8 +285,8 @@ public class FMLCommonHandler Loader.instance().serverStopping(); } - public ISaveFormat getSaveFormat() { - return sidedDelegate.getSaveFormat(); + public File getSavesDirectory() { + return sidedDelegate.getSavesDirectory(); } public MinecraftServer getMinecraftServerInstance() @@ -385,6 +386,7 @@ public class FMLCommonHandler return; } handlerSet.add(handler); + handlerToCheck = new WeakReference(handler); // for confirmBackupLevelDatUse Map additionalProperties = Maps.newHashMap(); worldInfo.setAdditionalProperties(additionalProperties); for (ModContainer mc : Loader.instance().getModList()) @@ -400,22 +402,12 @@ public class FMLCommonHandler } } - public void confirmBackupLevelDatUse() + public void confirmBackupLevelDatUse(SaveHandler handler) { - // 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 - } + if (handlerToCheck == null || handlerToCheck.get() != handler) { + // only run if the save has been initially loaded + handlerToCheck = null; + return; } String text = "Forge Mod Loader detected that the backup level.dat is being used.\n\n" + diff --git a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java index 8aecde919..f2d264b63 100644 --- a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java +++ b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java @@ -16,9 +16,11 @@ import java.io.File; import java.security.cert.Certificate; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import net.minecraft.item.Item; import net.minecraft.nbt.NBTBase; @@ -217,15 +219,20 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai dataList.put(dataTag.getString("K"), dataTag.getInteger("V")); } + Set blockedIds = new HashSet(); + if (!tag.hasKey("BlockedItemIds")) // no blocked id info -> old 1.7 save { // old early 1.7 save potentially affected by the registry mapping bug // fix the ids the best we can... - GameData.fixBrokenIds(dataList); + GameData.fixBrokenIds(dataList, blockedIds); } // blocked ids - int[] blockedIds = tag.getIntArray("BlockedItemIds"); + for (int id : tag.getIntArray("BlockedItemIds")) + { + blockedIds.add(id); + } // block aliases Map blockAliases = new HashMap(); list = tag.getTagList("BlockAliases", 10); diff --git a/fml/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java b/fml/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java index 2000a8df1..0d4cf8b39 100644 --- a/fml/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java +++ b/fml/src/main/java/cpw/mods/fml/common/IFMLSidedHandler.java @@ -12,14 +12,13 @@ package cpw.mods.fml.common; +import java.io.File; import java.util.List; import java.util.Set; 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; @@ -39,7 +38,7 @@ public interface IFMLSidedHandler void finishServerLoading(); - ISaveFormat getSaveFormat(); + File getSavesDirectory(); MinecraftServer getServer(); diff --git a/fml/src/main/java/cpw/mods/fml/common/LoadController.java b/fml/src/main/java/cpw/mods/fml/common/LoadController.java index 1f6131094..bbee723ab 100644 --- a/fml/src/main/java/cpw/mods/fml/common/LoadController.java +++ b/fml/src/main/java/cpw/mods/fml/common/LoadController.java @@ -130,7 +130,7 @@ public class LoadController FMLLog.severe("Fatal errors were detected during the transition from %s to %s. Loading cannot continue", oldState, desiredState); StringBuilder sb = new StringBuilder(); printModStates(sb); - FMLLog.severe(sb.toString()); + FMLLog.severe("%s", sb.toString()); if (errors.size()>0) { FMLLog.severe("The following problems were captured during this phase"); diff --git a/fml/src/main/java/cpw/mods/fml/common/ZipperUtil.java b/fml/src/main/java/cpw/mods/fml/common/ZipperUtil.java index c97297a44..0dfaefff8 100644 --- a/fml/src/main/java/cpw/mods/fml/common/ZipperUtil.java +++ b/fml/src/main/java/cpw/mods/fml/common/ZipperUtil.java @@ -80,7 +80,7 @@ public class ZipperUtil { public static void backupWorld(String dirName, String saveName) throws IOException { - File dstFolder = FMLCommonHandler.instance().getSaveFormat().getSaveLoader(dirName, false).getWorldDirectory().getParentFile(); + File dstFolder = FMLCommonHandler.instance().getSavesDirectory(); 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 diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java index da9871c15..cd679f5ca 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -14,9 +14,11 @@ package cpw.mods.fml.common.registry; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -229,7 +231,7 @@ public class GameData { * * @param dataList List containing the IDs to fix */ - public static void fixBrokenIds(Map dataList) + public static void fixBrokenIds(Map dataList, Set blockedIds) { BitSet availabilityMap = new BitSet(32000); @@ -237,6 +239,7 @@ public class GameData { for (Entry entry : dataList.entrySet()) { String itemName = entry.getKey(); + String realName = itemName.substring(1); if (itemName.charAt(0) == '\u0001') // is a block { @@ -244,7 +247,8 @@ public class GameData { } } - Set itemsToAllocate = new HashSet(); + Set newBlockedIds = new HashSet(); + Set itemsToRemove = new HashSet(); Map itemsToRelocate = new HashMap(); // check all ids occupied by items @@ -255,40 +259,76 @@ public class GameData { if (itemName.charAt(0) != '\u0001') // is an item { int oldId = entry.getValue(); + String realName = itemName.substring(1); + String blockName = '\u0001' + realName; + Item item = getMain().iItemRegistry.getRaw(realName); + boolean blockThisId = false; // block oldId unless it's used by a block - if (availabilityMap.get(oldId)) // id is already occupied + if (item == null) // item no longer available { - String realName = itemName.substring(1); - String blockName = '\u0001' + realName; + // can't fix items without reliably checking if they are ItemBlocks + FMLLog.warning("Item %s (old id %d) is no longer available and thus can't be fixed.", realName, oldId); + itemsToRemove.add(itemName); + blockThisId = true; + } + else if (item instanceof ItemBlock) + { + if (dataList.containsKey(blockName)) // the item was an ItemBlock before + { + int blockId = dataList.get(blockName); - if (!dataList.containsKey(blockName) || - (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 - { - // allocate the item later, after all correct ids have been claimed - itemsToAllocate.add(itemName); + if (blockId != oldId) // mis-located ItemBlock + { + // relocate to the matching block + FMLLog.warning("ItemBlock %s (old id %d) doesn't have the same id as its block (%d).", realName, oldId, blockId); + itemsToRelocate.put(entry.getKey(), blockId); + blockThisId = true; + } + else // intact ItemBlock + { + availabilityMap.set(oldId); // occupy id + } } - else if (dataList.get(blockName) != oldId) // occupied, but this is an ItemBlock for a different block than whatever may use its id + else // the item hasn't been an ItemBlock before, but it's now { - // relocate to the matching block - int newId = dataList.get(blockName); - itemsToRelocate.put(entry.getKey(), newId); + // can't fix these, drop them + FMLLog.warning("Item %s (old id %d) has been migrated to an ItemBlock and can't be fixed.", realName, oldId); + itemsToRemove.add(itemName); + blockThisId = true; } } - else // unused id, occupy + else if (availabilityMap.get(oldId)) // normal item, id is already occupied { + // remove the item mapping + FMLLog.warning("Item %s (old id %d) is conflicting with another block/item and can't be fixed.", realName, oldId); + itemsToRemove.add(itemName); + } + else // intact Item + { + availabilityMap.set(oldId); // occupy id + } + + // handle blocking the id from future use if possible (i.e. not used by a conflicting block) + // blockThisId requests don't modify availabilityMap, it could only be set by a block (or another item, which isn't being handled) + if (blockThisId && !availabilityMap.get(oldId)) + { + // there's no block occupying this id, thus block the id from future use + // as there may still be ItemStacks in the world referencing it + newBlockedIds.add(oldId); availabilityMap.set(oldId); } } } - if (itemsToAllocate.isEmpty() && itemsToRelocate.isEmpty()) return; // nothing to do + if (itemsToRemove.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."; + "A world backup will be created as a zip file in your saves\n" + + "directory automatically.\n\n" + + itemsToRemove.size()+" items need to be removed.\n"+ + itemsToRelocate.size()+" items need to be relocated."; boolean confirmed = StartupQuery.confirm(text); if (!confirmed) StartupQuery.abort(); @@ -303,14 +343,11 @@ public class GameData { StartupQuery.abort(); } - for (String itemName : itemsToAllocate) + for (String itemName : itemsToRemove) { - int oldId = dataList.get(itemName); - int newId = availabilityMap.nextClearBit(4096); + int id = dataList.remove(itemName); - dataList.put(itemName, newId); - - FMLLog.warning("Fixed Item %s conflicting with another block/item, old id %d, new id %d.", itemName.substring(1), oldId, newId); + FMLLog.warning("Removed Item %s, old id %d.", itemName.substring(1), id); } for (Map.Entry entry : itemsToRelocate.entrySet()) @@ -320,16 +357,18 @@ public class GameData { 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); + FMLLog.warning("Remapped Item %s to id %d, old id %d.", itemName.substring(1), newId, oldId); } + + blockedIds.addAll(newBlockedIds); } public static List injectWorldIDMap(Map dataList, boolean injectFrozenData, boolean isLocalWorld) { - return injectWorldIDMap(dataList, new int[0], new HashMap(), new HashMap(), injectFrozenData, isLocalWorld); + return injectWorldIDMap(dataList, new HashSet(), new HashMap(), new HashMap(), injectFrozenData, isLocalWorld); } - public static List injectWorldIDMap(Map dataList, int[] blockedIds, Map blockAliases, Map itemAliases, boolean injectFrozenData, boolean isLocalWorld) + public static List injectWorldIDMap(Map dataList, Set blockedIds, Map blockAliases, Map itemAliases, boolean injectFrozenData, boolean isLocalWorld) { FMLLog.info("Injecting existing block and item data into this %s instance", FMLCommonHandler.instance().getEffectiveSide().isServer() ? "server" : "client"); Map remaps = Maps.newHashMap(); @@ -390,11 +429,13 @@ public class GameData { if (currId != newId) { - throw new IllegalStateException(String.format("Can't map %s %s to id %d, already occupied by %s", + throw new IllegalStateException(String.format("Can't map %s %s to id %d, already occupied by %s, blocked %b, ItemBlock %b", isBlock ? "block" : "item", itemName, newId, - isBlock ? newData.iBlockRegistry.getObjectById(newId) : newData.iItemRegistry.getObjectById(newId))); + isBlock ? newData.iBlockRegistry.getRaw(newId) : newData.iItemRegistry.getRaw(newId), + newData.blockedIds.contains(newId), + isBlock ? getMain().iBlockRegistry.getRaw(currId) : getMain().iItemRegistry.getRaw(currId))); } } } diff --git a/fml/src/main/java/cpw/mods/fml/server/FMLServerHandler.java b/fml/src/main/java/cpw/mods/fml/server/FMLServerHandler.java index 6546c5a1e..726d7b379 100644 --- a/fml/src/main/java/cpw/mods/fml/server/FMLServerHandler.java +++ b/fml/src/main/java/cpw/mods/fml/server/FMLServerHandler.java @@ -12,6 +12,7 @@ */ package cpw.mods.fml.server; +import java.io.File; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -22,7 +23,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 net.minecraft.world.storage.SaveFormatOld; import com.google.common.collect.ImmutableList; @@ -32,7 +33,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.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.eventhandler.EventBus; import cpw.mods.fml.common.network.FMLNetworkEvent; import cpw.mods.fml.common.registry.LanguageRegistry; @@ -102,9 +102,9 @@ public class FMLServerHandler implements IFMLSidedHandler } @Override - public ISaveFormat getSaveFormat() + public File getSavesDirectory() { - return server.getActiveAnvilConverter(); + return ((SaveFormatOld) server.getActiveAnvilConverter()).savesDirectory; } /** diff --git a/fml/src/main/resources/fml_at.cfg b/fml/src/main/resources/fml_at.cfg index f92942df2..8b54a7fdc 100644 --- a/fml/src/main/resources/fml_at.cfg +++ b/fml/src/main/resources/fml_at.cfg @@ -78,6 +78,8 @@ public net.minecraft.client.Minecraft field_71446_o # textureManager public net.minecraft.item.ItemBlock field_150939_a ## DedicatedServer public net.minecraft.server.dedicated.DedicatedServer field_71341_l # pendingCommandList +## SaveFormatOld +public net.minecraft.world.storage.SaveFormatOld field_75808_a # savesDirectory protected net.minecraft.util.ObjectIntIdentityMap field_148749_a # internal map protected net.minecraft.util.ObjectIntIdentityMap field_148748_b # internal index list