public static List<String> processIdRematches( Iterable<MissingMapping> missedMappings, boolean isLocalWorld, GameData gameData, Map<String, Integer[]> remapBlocks, Map<String, Integer[]> remapItems) { List<String> failed = Lists.newArrayList(); List<String> ignored = Lists.newArrayList(); List<String> warned = Lists.newArrayList(); List<String> defaulted = Lists.newArrayList(); for (MissingMapping remap : missedMappings) { FMLMissingMappingsEvent.Action action = remap.getAction(); if (action == FMLMissingMappingsEvent.Action.REMAP) { // block/item re-mapped, finish the registration with the new name/object, but the old id int currId, newId; String newName; if (remap.type == Type.BLOCK) { currId = getMain().iBlockRegistry.getId((Block) remap.getTarget()); newName = getMain().iBlockRegistry.getNameForObject(remap.getTarget()).toString(); FMLLog.fine("The Block %s is being remapped to %s.", remap.name, newName); newId = gameData.registerBlock((Block) remap.getTarget(), newName, remap.id); gameData.iBlockRegistry.addAlias(remap.name, newName); } else { currId = getMain().iItemRegistry.getId((Item) remap.getTarget()); newName = getMain().iItemRegistry.getNameForObject(remap.getTarget()).toString(); FMLLog.fine("The Item %s is being remapped to %s.", remap.name, newName); newId = gameData.registerItem((Item) remap.getTarget(), newName, remap.id); gameData.iItemRegistry.addAlias(remap.name, newName); } if (newId != remap.id) throw new IllegalStateException(); if (currId != newId) { FMLLog.info( "Fixed %s id mismatch %s: %d (init) -> %d (map).", remap.type == Type.BLOCK ? "block" : "item", newName, currId, newId); (remap.type == Type.BLOCK ? remapBlocks : remapItems) .put(newName, new Integer[] {currId, newId}); } } else if (action == FMLMissingMappingsEvent.Action.BLOCKONLY) { // Pulled out specifically so the block doesn't get reassigned a new ID just because it's // Item block has gone away FMLLog.fine( "The ItemBlock %s is no longer present in the game. The residual block will remain", remap.name); } else { // block item missing, warn as requested and block the id if (action == FMLMissingMappingsEvent.Action.DEFAULT) { defaulted.add(remap.name); } else if (action == FMLMissingMappingsEvent.Action.IGNORE) { ignored.add(remap.name); } else if (action == FMLMissingMappingsEvent.Action.FAIL) { failed.add(remap.name); } else if (action == FMLMissingMappingsEvent.Action.WARN) { warned.add(remap.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 { String skip = System.getProperty("fml.doNotBackup"); if (skip == null || !"true".equals(skip)) { ZipperUtil.backupWorld(); } else { for (int x = 0; x < 10; x++) FMLLog.severe("!!!!!!!!!! UPDATING WORLD WITHOUT DOING BACKUP !!!!!!!!!!!!!!!!"); } } 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"); return failed; } if (!warned.isEmpty()) { FMLLog.severe("This world contains block and item mappings that may cause world breakage"); return failed; } else if (!ignored.isEmpty()) { FMLLog.fine("There were %d missing mappings that have been ignored", ignored.size()); } return failed; }
public static List<String> injectSnapshot( GameDataSnapshot snapshot, boolean injectFrozenData, boolean isLocalWorld) { FMLLog.info( "Injecting existing block and item data into this %s instance", FMLCommonHandler.instance().getEffectiveSide().isServer() ? "server" : "client"); Map<String, Integer[]> remapBlocks = Maps.newHashMap(); Map<String, Integer[]> remapItems = Maps.newHashMap(); LinkedHashMap<String, Integer> missingBlocks = new LinkedHashMap<String, Integer>(); LinkedHashMap<String, Integer> missingItems = new LinkedHashMap<String, Integer>(); getMain().testConsistency(); getMain().iBlockRegistry.dump(); getMain().iItemRegistry.dump(); getMain().iItemRegistry.resetSubstitutionDelegates(); GameDataSnapshot.Entry blocks = snapshot.entries.get("fml:blocks"); GameDataSnapshot.Entry items = snapshot.entries.get("fml:items"); GameData newData = new GameData(); for (int id : blocks.blocked) { newData.block(id); } for (Map.Entry<String, String> entry : blocks.aliases.entrySet()) { newData.iBlockRegistry.addAlias(entry.getKey(), entry.getValue()); } for (Map.Entry<String, String> entry : items.aliases.entrySet()) { newData.iItemRegistry.addAlias(entry.getKey(), entry.getValue()); } for (String entry : blocks.substitutions) { newData.iBlockRegistry.activateSubstitution(entry); } for (String entry : items.substitutions) { newData.iItemRegistry.activateSubstitution(entry); } if (injectFrozenData) { for (String newBlockSubstitution : getMain().blockSubstitutions.keySet()) { if (!blocks.substitutions.contains(newBlockSubstitution)) { newData.iBlockRegistry.activateSubstitution(newBlockSubstitution); } } for (String newItemSubstitution : getMain().itemSubstitutions.keySet()) { if (!items.substitutions.contains(newItemSubstitution)) { newData.iItemRegistry.activateSubstitution(newItemSubstitution); } } } // Clear State map for it's ready for us to register below. GameData.BLOCKSTATE_TO_ID.clear(); // process blocks and items in the world, blocks in the first pass, items in the second // blocks need to be added first for proper ItemBlock handling for (int pass = 0; pass < 2; pass++) { boolean isBlock = (pass == 0); Map<String, Integer> ids = (isBlock ? blocks.ids : items.ids); for (Entry<String, Integer> entry : ids.entrySet()) { String itemName = entry.getKey(); int newId = entry.getValue(); int currId = isBlock ? getMain().iBlockRegistry.getId(itemName) : getMain().iItemRegistry.getId(itemName); if (currId == -1) { FMLLog.info("Found a missing id from the world %s", itemName); (isBlock ? missingBlocks : missingItems).put(entry.getKey(), newId); continue; // no block/item -> nothing to add } else if (currId != newId) { FMLLog.fine( "Fixed %s id mismatch %s: %d (init) -> %d (map).", isBlock ? "block" : "item", itemName, currId, newId); (isBlock ? remapBlocks : remapItems).put(itemName, new Integer[] {currId, newId}); } // register if (isBlock) { currId = newData.registerBlock(getMain().iBlockRegistry.getRaw(itemName), itemName, newId); } else { currId = newData.registerItem(getMain().iItemRegistry.getRaw(itemName), itemName, newId); } if (currId != newId) { throw new IllegalStateException( String.format( "Can't map %s %s to id %d (seen at: %d), already occupied by %s, blocked %b, ItemBlock %b", isBlock ? "block" : "item", itemName, newId, currId, isBlock ? newData.iBlockRegistry.getRaw(newId) : newData.iItemRegistry.getRaw(newId), newData.blockedIds.contains(newId), isBlock ? false : (getMain().iItemRegistry.getRaw(currId) instanceof ItemBlock))); } } } List<String> missedMappings = Loader.instance() .fireMissingMappingEvent( missingBlocks, missingItems, isLocalWorld, newData, remapBlocks, remapItems); if (!missedMappings.isEmpty()) return missedMappings; // If we got here - the load was accepted. We'll load generic repositories here. // Generic registries can fail by returning a missing mapping. missedMappings = newData.loadGenericRegistries(snapshot, getMain()); if (!missedMappings.isEmpty()) return missedMappings; if (injectFrozenData) // add blocks + items missing from the map { Map<String, Integer> newBlocks = frozen.iBlockRegistry.getEntriesNotIn(newData.iBlockRegistry); Map<String, Integer> newItems = frozen.iItemRegistry.getEntriesNotIn(newData.iItemRegistry); if (!newBlocks.isEmpty() || !newItems.isEmpty()) { FMLLog.info("Injecting new block and item data into this server instance."); for (int pass = 0; pass < 2; pass++) { boolean isBlock = pass == 0; Map<String, Integer> missing = (pass == 0) ? newBlocks : newItems; Map<String, Integer[]> remaps = (isBlock ? remapBlocks : remapItems); for (Entry<String, Integer> entry : missing.entrySet()) { String itemName = entry.getKey(); int currId = entry.getValue(); int newId; if (isBlock) { newId = newData.registerBlock(frozen.iBlockRegistry.getRaw(itemName), itemName, currId); } else { newId = newData.registerItem(frozen.iItemRegistry.getRaw(itemName), itemName, currId); } FMLLog.info( "Injected new block/item %s: %d (init) -> %d (map).", itemName, currId, newId); if (newId != currId) // a new id was assigned { remaps.put(itemName, new Integer[] {entry.getValue(), newId}); } } } } } newData.testConsistency(); getMain().set(newData); getMain().iBlockRegistry.dump(); getMain().iItemRegistry.dump(); Loader.instance().fireRemapEvent(remapBlocks, remapItems); // The id map changed, ensure we apply object holders ObjectHolderRegistry.INSTANCE.applyObjectHolders(); return ImmutableList.of(); }