def console_scan_regionset(regionset, processes, entity_limit, remove_entities, verbose):
    """ Scan a regionset printing status to console.

    Uses AsyncRegionsetScanner.
    """

    rs = AsyncRegionsetScanner(regionset, processes, entity_limit, remove_entities)
    scanners = [rs]
    titles = [entitle("Scanning separate region files", 0)]
    console_scan_loop(scanners, titles, verbose)
    regionset.scanned = True
def console_scan_regionset(regionset, processes, entity_limit, remove_entities,
                           verbose):
    """ Scan a regionset printing status to console.

    Uses AsyncRegionsetScanner.
    """

    rs = AsyncRegionsetScanner(regionset, processes, entity_limit,
                               remove_entities)
    scanners = [rs]
    titles = [entitle("Scanning separate region files", 0)]
    console_scan_loop(scanners, titles, verbose)
    regionset.scanned = True
Exemple #3
0
def console_scan_regionset(regionset, processes, entity_limit, remove_entities,
                           verbose):
    """ Scan a regionset printing status to console.

    Inputs:
     - regionset -- RegionSet object from world.py that will be scanned
     - processes -- An integer with the number of child processes to use
     - entity_limit -- An integer, threshold of entities for a chunk to be considered
                     with too many entities
     - remove_entities -- A boolean, defaults to False, to remove the entities whilel 
                         scanning. This is really handy because opening chunks with
                         too many entities for scanning can take minutes.
     - verbose -- Boolean, if true it will print a line per scanned region file.

    """

    rs = AsyncRegionsetScanner(regionset, processes, entity_limit,
                               remove_entities)
    scanners = [rs]
    titles = [entitle("Scanning separate region files", 0)]
    console_scan_loop(scanners, titles, verbose)
    regionset.scanned = True
def main():
    usage = ('usage: \n%prog [options] <world-path> '
             '<other-world-path> ... <region-files> ...')
    epilog = ('Copyright (C) 2011  Alejandro Aguilera (Fenixin)\n'
              'https://github.com/Fenixin/Minecraft-Region-Fixer\n'
              'This program comes with ABSOLUTELY NO WARRANTY; for '
              'details see COPYING.txt. This is free software, and you '
              'are welcome to redistribute it under certain conditions; '
              'see COPYING.txt for details.')

    parser = OptionParser(description=('Program to check the integrity of '
                                       'Minecraft worlds and fix them when '
                                       'possible. It uses NBT by twoolie. '
                                       'Author: Alejandro Aguilera (Fenixin)'), prog='region_fixer', version=version_string, usage=usage, epilog=epilog)

    add_option = parser.add_option

    add_option('--backups', '-b', help=('List of backup directories of the Minecraft world '
                                        'to use to fix corrupted chunks and/or wrong located '
                                        'chunks. Warning! Region-Fixer is not going to check if'
                                        'it\'s the same world, be careful! This argument can be a'
                                        ' comma separated list (but never with spaces between '
                                        'elements!). This option can be only used scanning one '
                                        'world.'), metavar='<backups>', type=str, dest='backups', default=None)

    add_option('--replace-corrupted', '--rc', help='Tries to replace the corrupted chunks using the backup'
                                                   ' directories. This option can be only used scanning one'
                                                   ' world.', default=False, dest='replace_corrupted', action='store_true')

    add_option('--replace-wrong-located', '--rw', help='Tries to replace the wrong located chunks using the '
                                                       'backup directories. This option can be only used scanning'
                                                       ' one world.', default=False, dest='replace_wrong_located', action='store_true')

    add_option('--replace-entities', '--re', help='Tries to replace the chunks with too many entities using '
                                                  'the backup directories. This option can be only used '
                                                  'scanning one world.', default=False, dest='replace_entities', action='store_true')

    add_option('--replace-shared-offset', '--rs', help='Tries to replace the chunks with a shared offset using '
                                                       'the backup directories. This option can be only used'
                                                       'scanning one world.', default=False, dest='replace_shared_offset', action='store_true')

    add_option('--replace-too-small', '--rt', help='Tries to replace the region files that are too small to '
                                                   'be actually be a region file using the backup '
                                                   'directories. This option can be only used scanning one '
                                                   'world.', default=False, dest='replace_too_small', action='store_true')

    add_option('--delete-corrupted', '--dc', help='[WARNING!] This option deletes! This option will delete '
                                                  'all the corrupted chunks. Used with --replace-corrupted '
                                                  'or --replace-wrong-located it will delete all the '
                                                  'non-replaced chunks.', action='store_true', default=False)

    add_option('--delete-wrong-located', '--dw', help=('[WARNING!] This option deletes!'
                                                       'The same as --delete-corrupted but for wrong '
                                                       'located chunks'), action='store_true', default=False, dest='delete_wrong_located')

    add_option('--delete-entities', '--de', help='[WARNING!] This option deletes! This option deletes ALL '
                                                 'the entities in chunks with more entities than '
                                                 '--entity-limit (300 by default). In a Minecraft '
                                                 'entities are mostly mobs and items dropped in the '
                                                 'grond, items in chests and other stuff won\'t be '
                                                 'touched. Read the README for more info. Region-Fixer '
                                                 'will delete the entities while scanning so you can '
                                                 'stop and resume the process', action='store_true', default=False, dest='delete_entities')

    add_option('--delete-shared-offset', '--ds', help='[WARNING!] This option deletes! This option will delete '
                                                      'all the chunk with status shared offset. It will remove '
                                                      'the region header for the false chunk, note that you '
                                                      'don\'t loos any chunk doing this.', action='store_true', default=False, dest='delete_shared_offset')

    add_option('--delete-too-small', '--dt', help='[WARNING!] This option deletes! Removes any region files '
                                                  'found to be too small to actually be a region file.', dest='delete_too_small', default=False, action='store_true')

    add_option('--entity-limit', '--el', help='Specify the limit for the --delete-entities option '
                                              '(default = 300).', dest='entity_limit', default=300, action='store', type=int)

    add_option('--processes', '-p', help='Set the number of workers to use for scanning. (defaulta '
                                         '= 1, not use multiprocessing at all)', action='store', type=int, default=1)

    add_option('--verbose', '-v', help='Don\'t use a progress bar, instead print a line per '
                                       'scanned region file with results information. The '
                                       'letters mean c: corrupted; w: wrong located; t: total of '
                                       'chunksm; tme: too many entities problem', action='store_true', default=False)

    add_option('--interactive', '-i', help='Enter in interactive mode, where you can scan, see the '
                                           'problems, and fix them in a terminal like mode', dest='interactive', default=False, action='store_true', )

    add_option('--log', '-l', help='Saves a log of all the problems found in the spicifyed '
                                   'file. The log file contains all the problems found with '
                                   'this information: region file, chunk coordinates and '
                                   'problem. Use \'-\' as name to show the log at the end '
                                   'of the scan.', type=str, default=None, dest='summary')

    (options, args) = parser.parse_args()
    o = options

    if sys.version_info[0] > 2:
        print("")
        print("Minecraft Region Fixer only works with python 2.x")
        print("(And you just tried to run it in python {0})".format(sys.version))
        print("")
        return 1

    if is_bare_console():
        print("")
        print("Minecraft Region Fixer hast a command line aplication and a GUI\n"
              "(Graphic User Interface) and you have just double clicked the\n"
              "command line interface. If you really want to run the command line\n"
              "interface you have to use a command prompt (cmd.exe)\n\n"
              "You can also run the gui, double click regionfixer_gui.py instead!")
        print("")
        getpass("Press enter to continue:")
        return 1

    # Args are world_paths and region files
    if not args:
        parser.error('No world paths or region files specified! Use '
                     '--help for a complete list of options.')

    world_list, regionset = parse_paths(args)

    if not (world_list or regionset):
        print ("Error: No worlds or region files to scan!")
        return 1

    # Check basic options compatibilities
    any_chunk_replace_option = o.replace_corrupted or o.replace_wrong_located or o.replace_entities or o.replace_shared_offset
    any_chunk_delete_option = o.delete_corrupted or o.delete_wrong_located or o.delete_entities or o.delete_shared_offset
    any_region_replace_option = o.replace_too_small
    any_region_delete_option = o.delete_too_small

    error = parser.error

    # All scanners will use this progress bar
    widgets = ['Scanning: ', FractionWidget(), ' ', progressbar.Percentage(), ' ', progressbar.Bar(left='[', right=']'), ' ', progressbar.ETA()]

    if o.interactive or o.summary:
        if any_chunk_replace_option or any_region_replace_option:
            error('Can\'t use the options --replace-* , --delete-* and '
                  '--log with --interactive. You can choose all this '
                  'while in the interactive mode.')

    else:
        # Not options.interactive
        if o.backups:
            if not any_chunk_replace_option and not any_region_replace_option:
                error('The option --backups needs at least one of the '
                      '--replace-* options')
            else:
                if (len(regionset.regions) > 0):
                    error('You can\'t use the replace options while scanning '
                          'sparate region files. The input should be only one '
                          'world and you intruduced {0} individual region '
                          'files.'.format(len(regionset.regions)))
                elif (len(world_list) > 1):
                    error('You can\'t use the replace options while scanning '
                          'multiple worlds. The input should be only one '
                          'world and you intruduced {0} '
                          'worlds.'.format(len(world_list)))

        if not o.backups and any_chunk_replace_option:
            error("The options --replace-* need the --backups option")

    if o.entity_limit < 0:
        error("The entity limit must be at least 0!")

    print("\nWelcome to Region Fixer!")
    print("(version: {0})".format(parser.version))

    # Do things with the option options args
    # Create a list of worlds containing the backups of the region files
    if o.backups:
        backup_worlds = parse_backup_list(o.backups)
        if not backup_worlds:
            print ('[WARNING] No valid backup directories found, won\'t fix '
                   'any chunk.')
    else:
        backup_worlds = []

    # The program starts
    if o.interactive:
        c = InteractiveLoop(world_list, regionset, o, backup_worlds)
        c.cmdloop()
    else:
        summary_text = ""
        # Scan the separate region files
        if len(regionset.regions) > 0:
            console_scan_regionset(regionset, o.processes, o.entity_limit, o.delete_entities, o.verbose)
            print(regionset.generate_report(True))

            # Delete chunks
            delete_bad_chunks(options, regionset)

            # Delete region files
            delete_bad_regions(options, regionset)

            # Verbose log
            if options.summary:
                summary_text += "\n"
                summary_text += entitle("Separate region files")
                summary_text += "\n"
                t = regionset.summary()
                if t:
                    summary_text += t
                else:
                    summary_text += "No problems found.\n\n"

        # scan all the world folders
        for w in world_list:
            w_name = w.get_name()
            print(entitle(' Scanning world: {0} '.format(w_name), 0))

            console_scan_world(w, o.processes, o.entity_limit, o.delete_entities, o.verbose)

            print("")
            print(entitle('Scan results for: {0}'.format(w_name), 0))
            print(w.generate_report(True))

            #             corrupted, wrong_located, entities_prob, shared_prob,\
            #             total_chunks, too_small_region, unreadable_region, total_regions\
            #             = w.generate_report(standalone = False)

            print("")
            # Replace chunks
            if backup_worlds and not len(world_list) > 1:
                del_ent = options.delete_entities
                ent_lim = options.entity_limit
                options_replace = [o.replace_corrupted, o.replace_wrong_located, o.replace_entities, o.replace_shared_offset]
                replacing = zip(options_replace, world.CHUNK_PROBLEMS_ITERATOR)
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_chunks(problem)
                        if total:
                            text = " Replacing chunks with status: {0} ".format(status)
                            print("{0:#^60}".format(text))
                            fixed = w.replace_problematic_chunks(backup_worlds, problem, ent_lim, del_ent)
                            print("\n{0} replaced of a total of {1} chunks with status: {2}".format(fixed, total, status))
                        else:
                            print("No chunks to replace with status: {0}".format(status))

            elif any_chunk_replace_option and not backup_worlds:
                print("Info: Won't replace any chunk.")
                print("No backup worlds found, won't replace any chunks/region files!")
            elif any_chunk_replace_option and backup_worlds and len(world_list) > 1:
                print("Info: Won't replace any chunk.")
                print("Can't use the replace options while scanning more than one world!")

            # replace region files
            if backup_worlds and not len(world_list) > 1:
                del_ent = options.delete_entities
                ent_lim = options.entity_limit
                options_replace = [o.replace_too_small]
                replacing = zip(options_replace, world.REGION_PROBLEMS_ITERATOR)
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_regions(problem)
                        if total:
                            text = " Replacing regions with status: {0} ".format(status)
                            print("{0:#^60}".format(text))
                            fixed = w.replace_problematic_regions(backup_worlds, problem, ent_lim, del_ent)
                            print("\n{0} replaced of a total of {1} regions with status: {2}".format(fixed, total, status))
                        else:
                            print("No region to replace with status: {0}".format(status))

            elif any_region_replace_option and not backup_worlds:
                print("Info: Won't replace any regions.")
                print("No valid backup worlds found, won't replace any chunks/region files!")
                print("Note: You probably inserted some backup worlds with the backup option but they are probably no valid worlds, the most common issue is wrong path.")
            elif any_region_replace_option and backup_worlds and len(world_list) > 1:
                print("Info: Won't replace any regions.")
                print("Can't use the replace options while scanning more than one world!")

            # delete chunks
            delete_bad_chunks(options, w)

            # delete region files
            delete_bad_regions(options, w)

            # print a summary for this world
            if options.summary:
                summary_text += w.summary()

        # verbose log text
        if options.summary == '-':
            print("\nPrinting log:\n")
            print(summary_text)
        elif options.summary != None:
            try:
                f = open(options.summary, 'w')
                f.write(summary_text)
                f.write('\n')
                f.close()
                print("Log file saved in \'{0}\'.".format(options.summary))
            except:
                print("Something went wrong while saving the log file!")

    return 0
def main():
    usage = ('%(prog)s [options] <world-path> '
             '<other-world-path> ... <region-files> ...')
    epilog = ('Copyright (C) 2020  Alejandro Aguilera (Fenixin)\n'
              'https://github.com/Fenixin/Minecraft-Region-Fixer\n'
              'This program comes with ABSOLUTELY NO WARRANTY; for '
              'details see COPYING.txt. This is free software, and you '
              'are welcome to redistribute it under certain conditions; '
              'see COPYING.txt for details.')

    parser = argparse.ArgumentParser(
        description=('Program to check the integrity of '
                     'Minecraft worlds and fix them when '
                     'possible. It uses NBT by twoolie. '
                     'Author: Alejandro Aguilera (Fenixin)'),
        prog='region_fixer',
        usage=usage,
        epilog=epilog)

    parser.add_argument(
        '--backups',
        '-b',
        help=('List of backup directories of the Minecraft world '
              'to use to fix corrupted chunks and/or wrong located '
              'chunks. Warning! Region-Fixer is not going to check if'
              'it\'s the same world, be careful! This argument can be a'
              ' comma separated list (but never with spaces between '
              'elements!). This option can be only used scanning one '
              'world.'),
        metavar='<backups>',
        type=str,
        dest='backups',
        default=None)

    parser.add_argument(
        '--replace-corrupted',
        '--rc',
        help='Try to replace the corrupted chunks using the backup'
        ' directories. Can be only used scanning one world.',
        default=False,
        dest='replace_corrupted',
        action='store_true')

    parser.add_argument(
        '--replace-wrong-located',
        '--rw',
        help='Try to replace the wrong located chunks using the '
        'backup directories. Can be only used scanning one '
        'world.',
        default=False,
        dest='replace_wrong_located',
        action='store_true')

    parser.add_argument(
        '--replace-entities',
        '--re',
        help='Try to replace the chunks with too many entities using '
        'the backup directories. Can be only used scanning '
        'one world.',
        default=False,
        dest='replace_entities',
        action='store_true')

    parser.add_argument(
        '--replace-shared-offset',
        '--rs',
        help='Try to replace the chunks with a shared offset using '
        'the backup directories. Can be only used scanning '
        'one world.',
        default=False,
        dest='replace_shared_offset',
        action='store_true')

    parser.add_argument(
        '--replace-too-small',
        '--rt',
        help='Try to replace the region files that are too small to '
        'be actually be a region file using the backup directories.'
        'Can be only used scanning one world.',
        default=False,
        dest='replace_too_small',
        action='store_true')

    parser.add_argument(
        '--delete-corrupted',
        '--dc',
        help='[WARNING!] This option deletes! Delete all the corrupted '
        'chunks. Used with --replace-corrupted or --replace-wrong-located'
        ' will delete all the non-replaced chunks.',
        action='store_true',
        default=False)

    parser.add_argument(
        '--delete-wrong-located',
        '--dw',
        help='[WARNING!] This option deletes! Delete all the wrong located '
        'chunks. Used with --replace-corrupted or --replace-wrong-located'
        ' will delete all the non-replaced chunks.',
        action='store_true',
        default=False,
        dest='delete_wrong_located')

    parser.add_argument('--delete-entities',
                        '--de',
                        help='[WARNING!] This option deletes! Delete ALL '
                        'the entities in chunks with more entities than '
                        '--entity-limit (300 by default). In a Minecraft '
                        'entities are mostly mobs and items dropped in the '
                        'ground, items in chests and other stuff won\'t be '
                        'touched. Read the README for more info. Region-Fixer '
                        'will delete the entities while scanning so you can '
                        'stop and resume the process',
                        action='store_true',
                        default=False,
                        dest='delete_entities')

    parser.add_argument(
        '--delete-shared-offset',
        '--ds',
        help='[WARNING!] This option deletes! Delete all the chunk '
        'with status shared offset. It will remove the region '
        'header for the false chunk, note that you '
        'don\'t loose any chunk doing this.',
        action='store_true',
        default=False,
        dest='delete_shared_offset')

    parser.add_argument(
        '--delete-missing-tag',
        '--dmt',
        help='[WARNING!] This option deletes! Remove any chunks '
        'with the mandatory entities tag missing.',
        dest='delete_missing_tag',
        default=False,
        action='store_true')

    parser.add_argument(
        '--fix-corrupted',
        '--fc',
        help='Try to fix chunks that are corrupted by extracting as much '
        'information as possible',
        dest='fix_corrupted',
        default=False,
        action='store_true')

    parser.add_argument(
        '--fix-missing-tag',
        '--fm',
        help='Fix chunks that have the Entities tag missing. This will add '
        'the missing tag.',
        dest='fix_missing_tag',
        default=False,
        action='store_true')

    parser.add_argument(
        '--fix-wrong-located',
        '--fw',
        help=
        'Fix chunks that are wrong located. This will save them in the coordinates '
        'stored in their data.',
        dest='fix_wrong_located',
        default=False,
        action='store_true')

    parser.add_argument(
        '--delete-too-small',
        '--dt',
        help='[WARNING!] This option deletes! Remove any region files '
        'found to be too small to actually be a region file.',
        dest='delete_too_small',
        default=False,
        action='store_true')

    parser.add_argument(
        '--entity-limit',
        '--el',
        help='Specify the limit for the --delete-entities option '
        '(default = 300).',
        dest='entity_limit',
        default=300,
        action='store',
        type=int)

    parser.add_argument(
        '--processes',
        '-p',
        help='Set the number of workers to use for scanning. (default '
        '= 1, not use multiprocessing at all)',
        action='store',
        type=int,
        default=1)

    status_abbr = ""
    for status in c.CHUNK_PROBLEMS:
        status_abbr += "{0}: {1}; ".format(c.CHUNK_PROBLEMS_ABBR[status],
                                           c.CHUNK_STATUS_TEXT[status])
    parser.add_argument(
        '--verbose',
        '-v',
        help=('Don\'t use a progress bar, instead print a line per '
              'scanned file with results information. The '
              'letters mean:\n') + status_abbr,
        action='store_true',
        default=False)

    #===========================================================================
    # parser.add_argument('--interactive',
    #                     '-i',
    #                     help='Enter in interactive mode, where you can scan, see the '
    #                          'problems, and fix them in a terminal like mode',
    #                     dest='interactive',
    #                     default=False,
    #                     action='store_true', )
    #===========================================================================

    parser.add_argument(
        '--log',
        '-l',
        help='Save a log of all the problems found in the specified '
        'file. The log file contains all the problems found with '
        'this information: region file, chunk coordinates and '
        'problem. Use \'-\' as name to show the log at the end '
        'of the scan.',
        type=str,
        default=None,
        dest='summary')

    parser.add_argument('paths',
                        help='List with world or region paths',
                        nargs='*')

    args = parser.parse_args()

    if sys.version_info[0] != 3:
        print("")
        print("Minecraft Region Fixer only works with python 3.x")
        print(("(And you just tried to run it in python {0})".format(
            sys.version)))
        print("")
        return c.RV_CRASH

    if is_bare_console():
        print("")
        print("Minecraft Region Fixer is a command line application and \n"
              "you have just double clicked it. If you really want to run \n"
              "the command line interface you have to use a command prompt.\n"
              "Run cmd.exe in the run window.\n\n")
        print("")
        getpass("Press enter to continue:")
        return c.RV_CRASH

    world_list, regionset = world.parse_paths(args.paths)

    # print greetings an version number
    print("\nWelcome to Region Fixer!")
    print(("(v {0})".format(version_string)))

    # Check if there are valid worlds to scan
    if not (world_list or regionset):
        print('Error: No worlds or region files to scan! Use '
              '--help for a complete list of options.')
        return c.RV_NOTHING_TO_SCAN

    # Check basic options compatibilities
    any_chunk_replace_option = args.replace_corrupted or \
        args.replace_wrong_located or \
        args.replace_entities or \
        args.replace_shared_offset
    any_region_replace_option = args.replace_too_small

    if False or args.summary:  # removed interactive mode args.interactive
        if any_chunk_replace_option or any_region_replace_option:
            parser.error(
                'Error: Can\'t use the options --replace-* , --delete-* with '
                '--log')

    else:
        # Not options.interactive
        if args.backups:
            if not any_chunk_replace_option and not any_region_replace_option:
                parser.error(
                    'Error: The option --backups needs at least one of the '
                    '--replace-* options')
            else:
                if len(regionset) > 0:
                    parser.error(
                        'Error: You can\'t use the replace options while scanning '
                        'separate region files. The input should be only one '
                        'world and you introduced {0} individual region '
                        'files.'.format(len(regionset)))
                elif len(world_list) > 1:
                    parser.error(
                        'Error: You can\'t use the replace options while scanning '
                        'multiple worlds. The input should be only one '
                        'world and you introduced {0} '
                        'worlds.'.format(len(world_list)))

        if not args.backups and any_chunk_replace_option:
            parser.error(
                "Error: The options --replace-* need the --backups option")

    if args.entity_limit < 0:
        parser.error("Error: The entity limit must be at least 0!")

    # Do things with the option options args
    # Create a list of worlds containing the backups of the region files
    if args.backups:
        backup_worlds = world.parse_backup_list(args.backups)
        if not backup_worlds:
            print('[WARNING] No valid backup directories found, won\'t fix '
                  'any chunk.')
    else:
        backup_worlds = []

    # The scanning process starts
    found_problems_in_regionsets = False
    found_problems_in_worlds = False
    if False:  # removed args.interactive
        ci = InteractiveLoop(world_list, regionset, args, backup_worlds)
        ci.cmdloop()
        return c.RV_OK
    else:
        summary_text = ""
        # Scan the separate region files

        if len(regionset) > 0:

            console_scan_regionset(regionset, args.processes,
                                   args.entity_limit, args.delete_entities,
                                   args.verbose)
            print((regionset.generate_report(True)))

            # Delete chunks
            delete_bad_chunks(args, regionset)

            # Delete region files
            delete_bad_regions(args, regionset)

            # fix chunks
            fix_bad_chunks(args, regionset)

            # Verbose log
            if args.summary:
                summary_text += "\n"
                summary_text += entitle("Separate region files")
                summary_text += "\n"
                t = regionset.summary()
                if t:
                    summary_text += t
                else:
                    summary_text += "No problems found.\n\n"

            # Check if problems have been found
            if regionset.has_problems:
                found_problems_in_regionsets = True

        # scan all the world folders

        for w in world_list:
            w_name = w.get_name()
            print((entitle(' Scanning world: {0} '.format(w_name), 0)))

            console_scan_world(w, args.processes, args.entity_limit,
                               args.delete_entities, args.verbose)

            print("")
            print((entitle('Scan results for: {0}'.format(w_name), 0)))
            print((w.generate_report(True)))
            print("")

            # Replace chunks
            if backup_worlds and len(world_list) <= 1:
                del_ent = args.delete_entities
                ent_lim = args.entity_limit
                options_replace = [
                    args.replace_corrupted, args.replace_wrong_located,
                    args.replace_entities, args.replace_shared_offset
                ]
                replacing = list(
                    zip(options_replace, c.CHUNK_PROBLEMS_ITERATOR))
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_chunks(problem)
                        if total:
                            text = " Replacing chunks with status: {0} ".format(
                                status)
                            print(("{0:#^60}".format(text)))
                            fixed = w.replace_problematic_chunks(
                                backup_worlds, problem, ent_lim, del_ent)
                            print((
                                "\n{0} replaced of a total of {1} chunks with status: {2}"
                                .format(fixed, total, status)))
                        else:
                            print(("No chunks to replace with status: {0}".
                                   format(status)))

            elif any_chunk_replace_option and not backup_worlds:
                print("Info: Won't replace any chunk.")
                print(
                    "No backup worlds found, won't replace any chunks/region files!"
                )
            elif any_chunk_replace_option and backup_worlds and len(
                    world_list) > 1:
                print("Info: Won't replace any chunk.")
                print(
                    "Can't use the replace options while scanning more than one world!"
                )

            # replace region files
            if backup_worlds and len(world_list) <= 1:
                del_ent = args.delete_entities
                ent_lim = args.entity_limit
                options_replace = [args.replace_too_small]
                replacing = list(
                    zip(options_replace, c.REGION_PROBLEMS_ITERATOR))
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_regions(problem)
                        if total:
                            text = " Replacing regions with status: {0} ".format(
                                status)
                            print(("{0:#^60}".format(text)))
                            fixed = w.replace_problematic_regions(
                                backup_worlds, problem, ent_lim, del_ent)
                            print((
                                "\n{0} replaced of a total of {1} regions with status: {2}"
                                .format(fixed, total, status)))
                        else:
                            print(("No region to replace with status: {0}".
                                   format(status)))

            elif any_region_replace_option and not backup_worlds:
                print("Info: Won't replace any regions.")
                print(
                    "No valid backup worlds found, won't replace any chunks/region files!"
                )
                print(
                    "Note: You probably inserted some backup worlds with the backup option but they are probably no valid worlds, the most common issue is wrong path."
                )
            elif any_region_replace_option and backup_worlds and len(
                    world_list) > 1:
                print("Info: Won't replace any regions.")
                print(
                    "Can't use the replace options while scanning more than one world!"
                )

            # delete chunks
            delete_bad_chunks(args, w)

            # delete region files
            delete_bad_regions(args, w)

            # fix chunks
            fix_bad_chunks(args, w)

            # print a summary for this world
            if args.summary:
                summary_text += w.summary()

            # check if problems have been found
            if w.has_problems:
                found_problems_in_worlds = True

        # verbose log text
        if args.summary == '-':
            print("\nPrinting log:\n")
            print(summary_text)
        elif args.summary is not None:
            try:
                f = open(args.summary, 'w')
                f.write(summary_text)
                f.write('\n')
                f.close()
                print(("Log file saved in \'{0}\'.".format(args.summary)))
            except:
                print("Something went wrong while saving the log file!")

    if found_problems_in_regionsets or found_problems_in_worlds:
        return c.RV_BAD_WORLD

    return c.RV_OK
Exemple #6
0
def main():

    usage = ('usage: \n%prog [options] <world-path> '
             '<other-world-path> ... <region-files> ...')
    epilog = ('Copyright (C) 2011  Alejandro Aguilera (Fenixin)\n'
              'https://github.com/Fenixin/Minecraft-Region-Fixer\n'
              'This program comes with ABSOLUTELY NO WARRANTY; for '
              'details see COPYING.txt. This is free software, and you '
              'are welcome to redistribute it under certain conditions; '
              'see COPYING.txt for details.')

    parser = OptionParser(description=('Program to check the integrity of '
                                       'Minecraft worlds and fix them when '
                                       'possible. It uses NBT by twoolie. '
                                       'Author: Alejandro Aguilera (Fenixin)'),
                          prog='region_fixer',
                          version=version_string,
                          usage=usage,
                          epilog=epilog)

    add_option = parser.add_option

    add_option('--backups',
               '-b',
               help=('List of backup directories of the Minecraft world '
                     'to use to fix corrupted chunks and/or wrong located '
                     'chunks. Warning! Region-Fixer is not going to check if'
                     'it\'s the same world, be careful! This argument can be a'
                     ' comma separated list (but never with spaces between '
                     'elements!). This option can be only used scanning one '
                     'world.'),
               metavar='<backups>',
               type=str,
               dest='backups',
               default=None)

    add_option('--replace-corrupted',
               '--rc',
               help='Tries to replace the corrupted chunks using the backup'
               ' directories. This option can be only used scanning one'
               ' world.',
               default=False,
               dest='replace_corrupted',
               action='store_true')

    add_option('--replace-wrong-located',
               '--rw',
               help='Tries to replace the wrong located chunks using the '
               'backup directories. This option can be only used scanning'
               ' one world.',
               default=False,
               dest='replace_wrong_located',
               action='store_true')

    add_option('--replace-entities',
               '--re',
               help='Tries to replace the chunks with too many entities using '
               'the backup directories. This option can be only used '
               'scanning one world.',
               default=False,
               dest='replace_entities',
               action='store_true')

    add_option('--replace-shared-offset',
               '--rs',
               help='Tries to replace the chunks with a shared offset using '
               'the backup directories. This option can be only used'
               'scanning one world.',
               default=False,
               dest='replace_shared_offset',
               action='store_true')

    add_option('--replace-too-small',
               '--rt',
               help='Tries to replace the region files that are too small to '
               'be actually be a region file using the backup '
               'directories. This option can be only used scanning one '
               'world.',
               default=False,
               dest='replace_too_small',
               action='store_true')

    add_option('--delete-corrupted',
               '--dc',
               help='[WARNING!] This option deletes! This option will delete '
               'all the corrupted chunks. Used with --replace-corrupted '
               'or --replace-wrong-located it will delete all the '
               'non-replaced chunks.',
               action='store_true',
               default=False)

    add_option('--delete-wrong-located',
               '--dw',
               help=('[WARNING!] This option deletes!'
                     'The same as --delete-corrupted but for wrong '
                     'located chunks'),
               action='store_true',
               default=False,
               dest='delete_wrong_located')

    add_option('--delete-entities',
               '--de',
               help='[WARNING!] This option deletes! This option deletes ALL '
               'the entities in chunks with more entities than '
               '--entity-limit (300 by default). In a Minecraft '
               'entities are mostly mobs and items dropped in the '
               'grond, items in chests and other stuff won\'t be '
               'touched. Read the README for more info. Region-Fixer '
               'will delete the entities while scanning so you can '
               'stop and resume the process',
               action='store_true',
               default=False,
               dest='delete_entities')

    add_option('--delete-shared-offset',
               '--ds',
               help='[WARNING!] This option deletes! This option will delete '
               'all the chunk with status shared offset. It will remove '
               'the region header for the false chunk, note that you '
               'don\'t loos any chunk doing this.',
               action='store_true',
               default=False,
               dest='delete_shared_offset')

    add_option('--delete-too-small',
               '--dt',
               help='[WARNING!] This option deletes! Removes any region files '
               'found to be too small to actually be a region file.',
               dest='delete_too_small',
               default=False,
               action='store_true')

    add_option('--entity-limit',
               '--el',
               help='Specify the limit for the --delete-entities option '
               '(default = 300).',
               dest='entity_limit',
               default=300,
               action='store',
               type=int)

    add_option('--processes',
               '-p',
               help='Set the number of workers to use for scanning. (defaulta '
               '= 1, not use multiprocessing at all)',
               action='store',
               type=int,
               default=1)

    add_option('--verbose',
               '-v',
               help='Don\'t use a progress bar, instead print a line per '
               'scanned region file with results information. The '
               'letters mean c: corrupted; w: wrong located; t: total of '
               'chunksm; tme: too many entities problem',
               action='store_true',
               default=False)

    add_option(
        '--interactive',
        '-i',
        help='Enter in interactive mode, where you can scan, see the '
        'problems, and fix them in a terminal like mode',
        dest='interactive',
        default=False,
        action='store_true',
    )

    add_option('--log',
               '-l',
               help='Saves a log of all the problems found in the spicifyed '
               'file. The log file contains all the problems found with '
               'this information: region file, chunk coordinates and '
               'problem. Use \'-\' as name to show the log at the end '
               'of the scan.',
               type=str,
               default=None,
               dest='summary')

    (options, args) = parser.parse_args()
    o = options

    if sys.version_info[0] > 2:
        print("")
        print("Minecraft Region Fixer only works with python 2.x")
        print("(And you just tried to run it in python {0})".format(
            sys.version))
        print("")
        return 1

    if is_bare_console():
        print("")
        print(
            "Minecraft Region Fixer hast a command line aplication and a GUI\n"
            "(Graphic User Interface) and you have just double clicked the\n"
            "command line interface. If you really want to run the command line\n"
            "interface you have to use a command prompt (cmd.exe)\n\n"
            "You can also run the gui, double click regionfixer_gui.py instead!"
        )
        print("")
        getpass("Press enter to continue:")
        return 1

    # Args are world_paths and region files
    if not args:
        parser.error('No world paths or region files specified! Use '
                     '--help for a complete list of options.')

    world_list, regionset = parse_paths(args)

    if not (world_list or regionset):
        print("Error: No worlds or region files to scan!")
        return 1

    # Check basic options compatibilities
    any_chunk_replace_option = o.replace_corrupted or \
                               o.replace_wrong_located or \
                               o.replace_entities or \
                               o.replace_shared_offset
    any_chunk_delete_option = o.delete_corrupted or \
                              o.delete_wrong_located or \
                              o.delete_entities or \
                              o.delete_shared_offset
    any_region_replace_option = o.replace_too_small
    any_region_delete_option = o.delete_too_small

    error = parser.error

    # All scanners will use this progress bar
    widgets = [
        'Scanning: ',
        FractionWidget(), ' ',
        progressbar.Percentage(), ' ',
        progressbar.Bar(left='[', right=']'), ' ',
        progressbar.ETA()
    ]

    if o.interactive or o.summary:
        if any_chunk_replace_option or any_region_replace_option:
            error('Can\'t use the options --replace-* , --delete-* and '
                  '--log with --interactive. You can choose all this '
                  'while in the interactive mode.')

    else:
        # Not options.interactive
        if o.backups:
            if not any_chunk_replace_option and not any_region_replace_option:
                error('The option --backups needs at least one of the '
                      '--replace-* options')
            else:
                if (len(regionset.regions) > 0):
                    error('You can\'t use the replace options while scanning '
                          'sparate region files. The input should be only one '
                          'world and you intruduced {0} individual region '
                          'files.'.format(len(regionset.regions)))
                elif (len(world_list) > 1):
                    error('You can\'t use the replace options while scanning '
                          'multiple worlds. The input should be only one '
                          'world and you intruduced {0} '
                          'worlds.'.format(len(world_list)))

        if not o.backups and any_chunk_replace_option:
            error("The options --replace-* need the --backups option")

    if o.entity_limit < 0:
        error("The entity limit must be at least 0!")

    print("\nWelcome to Region Fixer!")
    print("(version: {0})".format(parser.version))

    # Do things with the option options args
    # Create a list of worlds containing the backups of the region files
    if o.backups:
        backup_worlds = parse_backup_list(o.backups)
        if not backup_worlds:
            print('[WARNING] No valid backup directories found, won\'t fix '
                  'any chunk.')
    else:
        backup_worlds = []

    # The program starts
    if o.interactive:
        c = InteractiveLoop(world_list, regionset, o, backup_worlds)
        c.cmdloop()
    else:
        summary_text = ""
        # Scan the separate region files
        if len(regionset.regions) > 0:
            console_scan_regionset(regionset, o.processes, o.entity_limit,
                                   o.delete_entities, o.verbose)
            print(regionset.generate_report(True))

            # Delete chunks
            delete_bad_chunks(options, regionset)

            # Delete region files
            delete_bad_regions(options, regionset)

            # Verbose log
            if options.summary:
                summary_text += "\n"
                summary_text += entitle("Separate region files")
                summary_text += "\n"
                t = regionset.summary()
                if t:
                    summary_text += t
                else:
                    summary_text += "No problems found.\n\n"

        # scan all the world folders
        for w in world_list:
            w_name = w.get_name()
            print(entitle(' Scanning world: {0} '.format(w_name), 0))

            console_scan_world(w, o.processes, o.entity_limit,
                               o.delete_entities, o.verbose)

            print("")
            print(entitle('Scan results for: {0}'.format(w_name), 0))
            print(w.generate_report(True))

            #             corrupted, wrong_located, entities_prob, shared_prob,\
            #             total_chunks, too_small_region, unreadable_region, total_regions\
            #             = w.generate_report(standalone = False)

            print("")
            # Replace chunks
            if backup_worlds and not len(world_list) > 1:
                del_ent = options.delete_entities
                ent_lim = options.entity_limit
                options_replace = [
                    o.replace_corrupted, o.replace_wrong_located,
                    o.replace_entities, o.replace_shared_offset
                ]
                replacing = zip(options_replace, world.CHUNK_PROBLEMS_ITERATOR)
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_chunks(problem)
                        if total:
                            text = " Replacing chunks with status: {0} ".format(
                                status)
                            print("{0:#^60}".format(text))
                            fixed = w.replace_problematic_chunks(
                                backup_worlds, problem, ent_lim, del_ent)
                            print(
                                "\n{0} replaced of a total of {1} chunks with status: {2}"
                                .format(fixed, total, status))
                        else:
                            print(
                                "No chunks to replace with status: {0}".format(
                                    status))

            elif any_chunk_replace_option and not backup_worlds:
                print("Info: Won't replace any chunk.")
                print(
                    "No backup worlds found, won't replace any chunks/region files!"
                )
            elif any_chunk_replace_option and backup_worlds and len(
                    world_list) > 1:
                print("Info: Won't replace any chunk.")
                print(
                    "Can't use the replace options while scanning more than one world!"
                )

            # replace region files
            if backup_worlds and not len(world_list) > 1:
                del_ent = options.delete_entities
                ent_lim = options.entity_limit
                options_replace = [o.replace_too_small]
                replacing = zip(options_replace,
                                world.REGION_PROBLEMS_ITERATOR)
                for replace, (problem, status, arg) in replacing:
                    if replace:
                        total = w.count_regions(problem)
                        if total:
                            text = " Replacing regions with status: {0} ".format(
                                status)
                            print("{0:#^60}".format(text))
                            fixed = w.replace_problematic_regions(
                                backup_worlds, problem, ent_lim, del_ent)
                            print(
                                "\n{0} replaced of a total of {1} regions with status: {2}"
                                .format(fixed, total, status))
                        else:
                            print(
                                "No region to replace with status: {0}".format(
                                    status))

            elif any_region_replace_option and not backup_worlds:
                print("Info: Won't replace any regions.")
                print(
                    "No valid backup worlds found, won't replace any chunks/region files!"
                )
                print(
                    "Note: You probably inserted some backup worlds with the backup option but they are probably no valid worlds, the most common issue is wrong path."
                )
            elif any_region_replace_option and backup_worlds and len(
                    world_list) > 1:
                print("Info: Won't replace any regions.")
                print(
                    "Can't use the replace options while scanning more than one world!"
                )

            # delete chunks
            delete_bad_chunks(options, w)

            # delete region files
            delete_bad_regions(options, w)

            # print a summary for this world
            if options.summary:
                summary_text += w.summary()

        # verbose log text
        if options.summary == '-':
            print("\nPrinting log:\n")
            print(summary_text)
        elif options.summary != None:
            try:
                f = open(options.summary, 'w')
                f.write(summary_text)
                f.write('\n')
                f.close()
                print("Log file saved in \'{0}\'.".format(options.summary))
            except:
                print("Something went wrong while saving the log file!")

    return 0