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; }
/** * Fire a FMLMissingMappingsEvent to let mods determine how blocks/items defined in the world * save, but missing from the runtime, are to be handled. * * @param missing Map containing missing names with their associated id, blocks need to come * before items for remapping. * @param isLocalWorld Whether this is executing for a world load (local/server) or a client. * @param gameData GameData instance where the new map's config is to be loaded into. * @return List with the mapping results. */ public List<String> fireMissingMappingEvent( LinkedHashMap<String, Integer> missingBlocks, LinkedHashMap<String, Integer> missingItems, boolean isLocalWorld, GameData gameData, Map<String, Integer[]> remapBlocks, Map<String, Integer[]> remapItems) { if (missingBlocks.isEmpty() && missingItems.isEmpty()) // nothing to do { return ImmutableList.of(); } FMLLog.fine( "There are %d mappings missing - attempting a mod remap", missingBlocks.size() + missingItems.size()); ArrayListMultimap<String, MissingMapping> missingMappings = ArrayListMultimap.create(); for (Map.Entry<String, Integer> mapping : missingBlocks.entrySet()) { MissingMapping m = new MissingMapping(GameRegistry.Type.BLOCK, mapping.getKey(), mapping.getValue()); missingMappings.put(m.name.substring(0, m.name.indexOf(':')), m); } for (Map.Entry<String, Integer> mapping : missingItems.entrySet()) { MissingMapping m = new MissingMapping(GameRegistry.Type.ITEM, mapping.getKey(), mapping.getValue()); missingMappings.put(m.name.substring(0, m.name.indexOf(':')), m); } FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent(missingMappings); modController.propogateStateMessage(missingEvent); if (isLocalWorld) // local world, warn about entries still being set to the default action { boolean didWarn = false; for (MissingMapping mapping : missingMappings.values()) { if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT) { if (!didWarn) { FMLLog.severe( "There are unidentified mappings in this world - we are going to attempt to process anyway"); didWarn = true; } FMLLog.severe( "Unidentified %s: %s, id %d", mapping.type == Type.BLOCK ? "block" : "item", mapping.name, mapping.id); } } } else // remote world, fail on entries with the default action { List<String> missedMapping = new ArrayList<String>(); for (MissingMapping mapping : missingMappings.values()) { if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT) { missedMapping.add(mapping.name); } } if (!missedMapping.isEmpty()) { return ImmutableList.copyOf(missedMapping); } } return GameData.processIdRematches( missingMappings.values(), isLocalWorld, gameData, remapBlocks, remapItems); }