예제 #1
0
파일: GameData.java 프로젝트: Kobata/FML
  public static List<String> processIdRematches(
      Iterable<MissingMapping> missedMappings,
      boolean isLocalWorld,
      GameData gameData,
      Map<String, Integer[]> remaps) {
    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());
          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());
          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);
          remaps.put(newName, new Integer[] {currId, newId});
        }
      } 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 {
        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");
      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;
  }
예제 #2
0
파일: GameData.java 프로젝트: Kobata/FML
  /**
   * Fix IDs improperly allocated by early versions of the registry, best-effort.
   *
   * <p>Items sharing the same ID with a block, but not sharing the same registry name will be
   * mapped to an unused id. Losing items instead of blocks should minimize the damage.
   *
   * @param dataList List containing the IDs to fix
   */
  public static void fixBrokenIds(Map<String, Integer> dataList, Set<Integer> blockedIds) {
    BitSet availabilityMap = new BitSet(MAX_ITEM_ID + 1);

    // reserve all ids occupied by blocks
    for (Entry<String, Integer> entry : dataList.entrySet()) {
      String itemName = entry.getKey();
      @SuppressWarnings("unused")
      String realName = itemName.substring(1);

      if (itemName.charAt(0) == '\u0001') // is a block
      {
        availabilityMap.set(entry.getValue());
      }
    }

    Set<Integer> newBlockedIds = new HashSet<Integer>();
    Set<String> itemsToRemove = new HashSet<String>();
    Map<String, Integer> itemsToRelocate = new HashMap<String, Integer>();

    // check all ids occupied by items
    for (Entry<String, Integer> entry : dataList.entrySet()) {
      String itemName = entry.getKey();

      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 (item == null) // item no longer available
        {
          // 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 (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 // the item hasn't been an ItemBlock before, but it's now
          {
            // 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 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 (itemsToRemove.isEmpty() && itemsToRelocate.isEmpty()) return; // nothing to do

    // confirm
    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.\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();

    // confirm missing mods causing item removal
    Set<String> modsMissing = new HashSet<String>();

    for (String itemName : itemsToRemove) {
      modsMissing.add(itemName.substring(1, itemName.indexOf(':')));
    }

    for (Iterator<String> it = modsMissing.iterator(); it.hasNext(); ) {
      String mod = it.next();

      if (mod.equals("minecraft") || Loader.isModLoaded(mod)) it.remove();
    }

    if (!modsMissing.isEmpty()) {
      text =
          "Forge Mod Loader detected that "
              + modsMissing.size()
              + " mods are missing.\n\n"
              + "If you continue items previously provided by those mods will be\n"
              + "removed while repairing this world save.\n\n"
              + "Missing mods:\n";

      for (String mod : modsMissing) text += mod + "\n";

      confirmed = StartupQuery.confirm(text);
      if (!confirmed) StartupQuery.abort();
    }

    // backup
    try {
      ZipperUtil.backupWorld();
    } catch (IOException e) {
      StartupQuery.notify("The world backup couldn't be created.\n\n" + e);
      StartupQuery.abort();
    }

    // apply fix
    for (String itemName : itemsToRemove) {
      int id = dataList.remove(itemName);

      FMLLog.warning("Removed Item %s, old id %d.", itemName.substring(1), id);
    }

    for (Map.Entry<String, Integer> entry : itemsToRelocate.entrySet()) {
      String itemName = entry.getKey();
      int newId = entry.getValue();

      int oldId = dataList.put(itemName, newId);

      FMLLog.warning("Remapped Item %s to id %d, old id %d.", itemName.substring(1), newId, oldId);
    }

    blockedIds.addAll(newBlockedIds);
  }