Exemplo n.º 1
0
def setup_failure_freeplay(setup_manager: SetupManager,
                           message: str,
                           color_key="red"):
    match_config = MatchConfig()
    match_config.game_mode = game_mode_types[0]
    match_config.game_map = "BeckwithPark"
    match_config.enable_rendering = True

    mutators = MutatorConfig()
    mutators.match_length = match_length_types[3]
    match_config.mutators = mutators

    match_config.player_configs = []

    setup_manager.load_match_config(match_config)
    setup_manager.start_match()
    color = getattr(setup_manager.game_interface.renderer, color_key)()
    setup_manager.game_interface.renderer.begin_rendering()
    # setup_manager.game_interface.renderer.draw_rect_2d(20, 20, 800, 800, True, setup_manager.game_interface.renderer.black())
    setup_manager.game_interface.renderer.draw_string_2d(
        20, 200, 4, 4, message, color)
    setup_manager.game_interface.renderer.end_rendering()
Exemplo n.º 2
0
def start_match(bot_list, match_settings):
    launcher_preference_map = load_launcher_settings()
    launcher_prefs = launcher_preferences_from_map(launcher_preference_map)

    # Show popup in GUI if rocket league is not started with -rlbot flag
    try:
        testSetupManager = SetupManager()
        temp, port = gateway_util.find_existing_process()
        if port is None:
            # RLBot.exe is not running, but Rocket League might be. That's a situation we can recover from, RLBot.exe
            # will be booted up using the ideal port later, so assume that port.
            port = gateway_util.IDEAL_RLBOT_PORT
        del temp
        # It's fine if rocket league is not running, this would just return false and we'd proceed. What we're checking
        # for is an exception thrown when rocket league IS running but without the necessary flags or a mismatched port.
        testSetupManager.is_rocket_league_running(port)
    except Exception as e:
        eel.noRLBotFlagPopup()
        print(
            f"Error starting match. This is probably due to Rocket League not being started under the -rlbot flag. {e}"
        )
    else:
        eel.spawn(start_match_helper, bot_list, match_settings, launcher_prefs)
Exemplo n.º 3
0
    def setup_match(self):
        # TODO This should be replaced?
        arguments = docopt(__doc__)

        bot_directory = arguments['--bot-folder']
        bundles = scan_directory_for_bot_configs(bot_directory)

        # Set up RLBot.cfg
        framework_config = create_bot_config_layout()
        config_location = os.path.join(os.path.dirname(__file__), 'rlbot.cfg')
        framework_config.parse_file(config_location, max_index=MAX_PLAYERS)
        match_config = parse_match_config(framework_config, config_location,
                                          {}, {})

        looks_configs = {
            idx: bundle.get_looks_config()
            for idx, bundle in enumerate(bundles)
        }
        names = [bundle.name for bundle in bundles]

        player_config = match_config.player_configs[0]
        match_config.player_configs.clear()
        for i in range(max(len(bundles), self.min_bots)):
            copied = PlayerConfig()
            copied.bot = player_config.bot
            copied.name = player_config.name
            copied.rlbot_controlled = player_config.rlbot_controlled
            copied.config_path = player_config.config_path
            copied.team = player_config.team if i % 2 == 0 else not player_config.team
            if i < len(bundles):
                copied.name = names[i]
                # If you want to override bot appearances to get a certain visual effect, e.g. with
                # specific boost colors, this is a good place to do it.
                copied.loadout_config = load_bot_appearance(
                    looks_configs[i], 0)
            match_config.player_configs.append(copied)

        manager = SetupManager()
        manager.load_match_config(match_config, {})
        manager.connect_to_game(
            RocketLeagueLauncherPreference(
                RocketLeagueLauncherPreference.STEAM, False))
        manager.start_match()
Exemplo n.º 4
0
    def __init__(self):
        match_config = MatchConfig()
        match_config.game_mode = 'Soccer'
        match_config.game_map = 'Mannfield_Night'
        match_config.existing_match_behavior = 'Continue And Spawn'
        match_config.mutators = MutatorConfig()
        match_config.mutators.match_length = 'Unlimited'

        # there needs to be at least one bot for the match to start
        bot_config = PlayerConfig()
        bot_config.name = "Dummy"
        bot_config.team = 0
        bot_config.bot = True
        bot_config.rlbot_controlled = True
        match_config.player_configs = [bot_config]

        sm = SetupManager()
        sm.connect_to_game()
        sm.load_match_config(match_config)
        sm.start_match()
        self.game_interface: GameInterface = sm.game_interface
Exemplo n.º 5
0
def start_match_helper(bot_list, match_settings):
    print(bot_list)
    print(match_settings)

    match_config = MatchConfig()
    match_config.game_mode = match_settings['game_mode']
    match_config.game_map = match_settings['map']
    match_config.skip_replays = match_settings['skip_replays']
    match_config.instant_start = match_settings['instant_start']
    match_config.enable_lockstep = match_settings['enable_lockstep']
    match_config.enable_rendering = match_settings['enable_rendering']
    match_config.enable_state_setting = match_settings['enable_state_setting']
    match_config.auto_save_replay = match_settings['auto_save_replay']
    match_config.existing_match_behavior = match_settings['match_behavior']
    match_config.mutators = MutatorConfig()

    mutators = match_settings['mutators']
    match_config.mutators.match_length = mutators['match_length']
    match_config.mutators.max_score = mutators['max_score']
    match_config.mutators.overtime = mutators['overtime']
    match_config.mutators.series_length = mutators['series_length']
    match_config.mutators.game_speed = mutators['game_speed']
    match_config.mutators.ball_max_speed = mutators['ball_max_speed']
    match_config.mutators.ball_type = mutators['ball_type']
    match_config.mutators.ball_weight = mutators['ball_weight']
    match_config.mutators.ball_size = mutators['ball_size']
    match_config.mutators.ball_bounciness = mutators['ball_bounciness']
    match_config.mutators.boost_amount = mutators['boost_amount']
    match_config.mutators.rumble = mutators['rumble']
    match_config.mutators.boost_strength = mutators['boost_strength']
    match_config.mutators.gravity = mutators['gravity']
    match_config.mutators.demolish = mutators['demolish']
    match_config.mutators.respawn_time = mutators['respawn_time']

    human_index_tracker = IncrementingInteger(0)
    match_config.player_configs = [create_player_config(bot, human_index_tracker) for bot in bot_list]
    match_config.script_configs = [create_script_config(script) for script in match_settings['scripts']]

    global sm
    if sm is not None:
        try:
            sm.shut_down()
        except Exception as e:
            print(e)

    sm = SetupManager()
    sm.early_start_seconds = 5
    sm.connect_to_game()
    sm.load_match_config(match_config)
    sm.launch_early_start_bot_processes()
    sm.start_match()
    sm.launch_bot_processes()
Exemplo n.º 6
0
def setup_match(manager: SetupManager):
    manager.shut_down(kill_all_pids=True, quiet=True)  # Stop any running pids
    manager.load_config()
    manager.launch_early_start_bot_processes()
    manager.start_match()
    manager.launch_bot_processes()
Exemplo n.º 7
0
def spawn_car_in_showroom(loadout_config: LoadoutConfig, team: int, showcase_type: str, map_name: str,
                          launcher_prefs: RocketLeagueLauncherPreference):
    match_config = MatchConfig()
    match_config.game_mode = 'Soccer'
    match_config.game_map = map_name
    match_config.instant_start = True
    match_config.existing_match_behavior = 'Continue And Spawn'
    match_config.networking_role = NetworkingRole.none
    match_config.enable_state_setting = True
    match_config.skip_replays = True

    bot_config = PlayerConfig()
    bot_config.bot = True
    bot_config.rlbot_controlled = True
    bot_config.team = team
    bot_config.name = "Showroom"
    bot_config.loadout_config = loadout_config

    match_config.player_configs = [bot_config]
    match_config.mutators = MutatorConfig()
    match_config.mutators.boost_amount = 'Unlimited'
    match_config.mutators.match_length = 'Unlimited'

    global sm
    if sm is None:
        sm = SetupManager()
    sm.connect_to_game(launcher_preference=launcher_prefs)
    sm.load_match_config(match_config)
    sm.start_match()

    game_state = GameState(
        cars={0: CarState(physics=Physics(
            location=Vector3(0, 0, 20),
            velocity=Vector3(0, 0, 0),
            angular_velocity=Vector3(0, 0, 0),
            rotation=Rotator(0, 0, 0)
        ))},
        ball=BallState(physics=Physics(
            location=Vector3(0, 0, -100),
            velocity=Vector3(0, 0, 0),
            angular_velocity=Vector3(0, 0, 0)
        ))
    )
    player_input = PlayerInput()
    team_sign = -1 if team == 0 else 1

    if showcase_type == "boost":
        player_input.boost = True
        player_input.steer = 1
        game_state.cars[0].physics.location.y = -1140
        game_state.cars[0].physics.velocity.x = 2300
        game_state.cars[0].physics.angular_velocity.z = 3.5

    elif showcase_type == "throttle":
        player_input.throttle = 1
        player_input.steer = 0.56
        game_state.cars[0].physics.location.y = -1140
        game_state.cars[0].physics.velocity.x = 1410
        game_state.cars[0].physics.angular_velocity.z = 1.5

    elif showcase_type == "back-center-kickoff":
        game_state.cars[0].physics.location.y = 4608 * team_sign
        game_state.cars[0].physics.rotation.yaw = -0.5 * pi * team_sign

    elif showcase_type == "goal-explosion":
        game_state.cars[0].physics.location.y = -2000 * team_sign
        game_state.cars[0].physics.rotation.yaw = -0.5 * pi * team_sign
        game_state.cars[0].physics.velocity.y = -2300 * team_sign
        game_state.ball.physics.location = Vector3(0, -3500 * team_sign, 93)

    sm.game_interface.update_player_input(player_input, 0)
    sm.game_interface.set_game_state(game_state)
Exemplo n.º 8
0
def main():

    print("starting")
    manager = SetupManager()
    manager.startup()
    manager.load_config()
    manager.init_ball_prediction()
    manager.launch_bot_processes()
    manager.run()  # Runs forever until interrupted
Exemplo n.º 9
0
def run_exercises(setup_manager: SetupManager,
                  exercises: Iterable[Exercise],
                  seed: int,
                  reload_agent: bool = True) -> Iterator[Result]:
    """
    It is recommended to use setup_manager_context() to generate your setup_manager.
    """
    game_interface = setup_manager.game_interface
    names = [exercise.get_name() for exercise in exercises]
    with training_status_renderer_context(names,
                                          game_interface.renderer) as ren:
        for i, exercise in enumerate(exercises):
            with safe_matchcomms_factory(setup_manager) as matchcomms_factory:

                def update_row(status: str, status_color_func):
                    nonlocal i
                    nonlocal exercise
                    ren.update(
                        i, Row(exercise.get_name(), status, status_color_func))

                update_row('config', ren.renderman.white)
                # Only reload the match if the config has changed.
                new_match_config = exercise.get_match_config()
                if new_match_config != setup_manager.match_config:
                    update_row('match', ren.renderman.white)
                    _setup_match(new_match_config, setup_manager)
                    update_row('bots', ren.renderman.white)
                    _wait_until_bots_ready(setup_manager, new_match_config)

                if reload_agent:
                    update_row('reload', ren.renderman.white)
                    setup_manager.reload_all_agents(quiet=True)

                # Briefing
                update_row('brief', ren.renderman.white)
                try:
                    exercise.set_matchcomms_factory(matchcomms_factory)
                    early_result = exercise.on_briefing()
                except Exception as e:
                    update_row('brief', ren.renderman.red)
                    yield Result(
                        exercise, seed,
                        FailDueToExerciseException(e, traceback.format_exc()))
                    continue
                if early_result is not None:
                    if isinstance(early_result.grade, Pass):
                        update_row('PASS', ren.renderman.green)
                    else:
                        update_row('FAIL', ren.renderman.red)
                    yield early_result
                    continue

                update_row('wait', ren.renderman.white)
                _wait_until_good_ticks(game_interface)

                update_row('setup', ren.renderman.white)
                error_result = _setup_exercise(game_interface, exercise, seed)
                if error_result is not None:
                    update_row('setup', ren.renderman.red)
                    yield error_result
                    continue

                # Wait for the set_game_state() to propagate before we start running ex.on_tick()
                # TODO: wait until the game looks similar.
                update_row('sleep', ren.renderman.white)
                time.sleep(0.03)

                update_row('>>>>', ren.renderman.white)
                result = _grade_exercise(game_interface, exercise, seed)

                if isinstance(result.grade, Pass):
                    update_row('PASS', ren.renderman.green)
                else:
                    update_row('FAIL', ren.renderman.red)
                yield result
Exemplo n.º 10
0
def main(gym=False):
    print(f"Starting Custom runner with gym:{gym}")
    check_python_version()
    manager = SetupManager(gym)
    manager.connect_to_game()
    manager.load_config()
    manager.launch_ball_prediction()
    manager.launch_quick_chat_manager()
    manager.launch_bot_processes()
    manager.start_match()
    return manager
Exemplo n.º 11
0
def main():

    print("starting")
    check_python_version()
    manager = SetupManager()
    manager.connect_to_game()
    manager.load_config()
    manager.launch_ball_prediction()
    manager.launch_quick_chat_manager()
    manager.launch_bot_processes()
    manager.start_match()
    manager.infinite_loop()  # Runs forever until interrupted
Exemplo n.º 12
0
def ensure_dll_is_injected():
    manager = SetupManager()
    manager.connect_to_game()
Exemplo n.º 13
0
def _wait_until_bots_ready(setup_manager: SetupManager, match_config: MatchConfig):
    logger = get_logger(DEFAULT_LOGGER)
    while not setup_manager.has_received_metadata_from_all_bots():
        logger.debug('Waiting on all bots to post their metadata.')
        setup_manager.try_recieve_agent_metadata()
        time.sleep(0.1)
Exemplo n.º 14
0
    def setup_match(self):
        # Set up RLBot.cfg
        framework_config = create_bot_config_layout()
        config_location = os.path.join(os.path.dirname(__file__), 'rlbot.cfg')
        framework_config.parse_file(config_location, max_index=MAX_PLAYERS)
        match_config = parse_match_config(framework_config, config_location,
                                          {}, {})
        match_config.game_map = self.choreo_obj.map_name

        # The three blocks of code below are basically identical.
        # TODO Make them into a function?

        # Gets appearance list from choreo.
        appearances = self.choreo_obj.get_appearances(self.min_bots)
        # Checks that it is the correct length.
        if len(appearances) != self.min_bots:
            print(
                '[RLBotChoreography]: Number of appearances does not match number of bots.'
            )
            print('[RLBotChoreography]: Using default appearances.')
            appearances = ['default.cfg'] * self.min_bots

        # Gets teams list from choreo.
        teams = self.choreo_obj.get_teams(self.min_bots)
        # Checks that it is the correct length.
        if len(teams) != self.min_bots:
            print(
                '[RLBotChoreography]: Number of teams does not match number of bots.'
            )
            print('[RLBotChoreography]: Putting all on blue.')
            teams = [0] * self.min_bots

        # Gets names list from choreo.
        names = self.choreo_obj.get_names(self.min_bots)
        # Checks that it is the correct length.
        if len(names) != self.min_bots:
            print(
                '[RLBotChoreography]: Number of names does not match number of bots.'
            )
            print('[RLBotChoreography]: Using bot indices as names.')
            names = range(self.min_bots)

        # Loads appearances.
        looks_configs = {
            idx: create_looks_configurations().parse_file(
                os.path.abspath('./ChoreographyHive/appearances/' + file_name))
            for idx, file_name in enumerate(appearances)
        }

        # rlbot.cfg specifies only one bot,
        # so we have to copy each and assign correct appearance.
        player_config = match_config.player_configs[0]
        match_config.player_configs.clear()
        for i in range(self.min_bots):
            copied = PlayerConfig()
            copied.name = names[i]
            copied.team = teams[i]
            copied.bot = player_config.bot
            copied.rlbot_controlled = player_config.rlbot_controlled
            copied.config_path = player_config.config_path
            copied.loadout_config = load_bot_appearance(
                looks_configs[i], copied.team)
            match_config.player_configs.append(copied)

        manager = SetupManager()
        manager.load_match_config(match_config, {})
        manager.connect_to_game()
        manager.start_match()
Exemplo n.º 15
0
def run_tests(my_queue: Queue):
    """Runs the tests."""
    check_python_version()
    manager = SetupManager()

    has_started = False

    for config in TESTS:
        if len(TESTS[config]) == 0:
            continue

        # Start a match.
        directory = os.path.abspath(os.path.dirname(__file__))
        config_location = os.path.join(directory, config, "match.cfg")
        manager.load_config(config_location=config_location)
        manager.connect_to_game()
        manager.launch_early_start_bot_processes()
        manager.start_match()
        manager.launch_bot_processes()

        if not has_started:
            # Let other thread know that game has been launched.
            my_queue.put(Message.READY)
            has_started = True

        # Load first test for this config.
        test_num = 0
        my_queue.put((get_game_state(
            os.path.join(directory, config, TESTS[config][test_num])), None))

        while not manager.quit_event.is_set():
            manager.try_recieve_agent_metadata()

            # Move onto the next test.
            if keyboard.is_pressed(NEXT_KEY):
                test_num += 1

                # If we have exceeded the number of tests in this config,
                # break and go to the next config.
                if len(TESTS[config]) <= test_num:
                    break

                # Loads the next test.
                my_queue.put((get_game_state(
                    os.path.join(directory, config,
                                 TESTS[config][test_num])), None))

                # Prevent accidental multiple key presses.
                time.sleep(1)

        # Kill all bot processes.
        manager.shut_down(kill_all_pids=True)

    my_queue.put((None, Message.DONE))
    print("Thread 1 closing.")
    exit()
Exemplo n.º 16
0
def run_challenge(match_config: MatchConfig, challenge: dict,
                  upgrades: dict) -> Tuple[bool, dict]:
    """Launch the game and keep track of the state"""
    setup_manager = SetupManager()
    setup_manager.early_start_seconds = 5
    setup_manager.connect_to_game()
    setup_manager.load_match_config(match_config)
    setup_manager.launch_early_start_bot_processes()
    setup_manager.start_match()
    setup_manager.launch_bot_processes()

    game_results = None
    try:
        game_results = manage_game_state(challenge, upgrades, setup_manager)
    except:
        # no matter what happens we gotta continue
        traceback.print_exc()
        print("Something failed with the game. Will proceed with shutdown")
        # need to make failure apparent to user
        setup_failure_freeplay(setup_manager, "The game failed to continue")
        return False, {}

    setup_manager.shut_down()

    return game_results
Exemplo n.º 17
0
def run_match(file_path):
    assert os.path.exists(file_path), f"Invalid path: {file_path}"
    _, ext = os.path.splitext(file_path)
    assert ext == ".cfg", f"Wrong file extension: '{ext}', expected '.cfg'"

    print(f"Using config at {file_path}")

    manager = SetupManager()
    manager.load_config(config_location=file_path)

    try:
        manager.connect_to_game()
        manager.launch_early_start_bot_processes()
        manager.start_match()
        manager.launch_bot_processes()
    except Exception:
        traceback.print_exc()
    finally:
        manager.shut_down(kill_all_pids=True)
Exemplo n.º 18
0
def _setup_match(match_config: MatchConfig, manager: SetupManager):
    manager.shut_down(kill_all_pids=True, quiet=True)  # To be safe.
    manager.load_match_config(match_config)
    manager.launch_early_start_bot_processes()
    manager.start_match()
    manager.launch_bot_processes()
Exemplo n.º 19
0
def wait_for_all_bots(manager: SetupManager):
    while not manager.has_received_metadata_from_all_bots():
        manager.try_recieve_agent_metadata()
        time.sleep(0.1)
Exemplo n.º 20
0
def record_atba():
    raw_config_parser = configparser.RawConfigParser()
    raw_config_parser.read(RLBOT_CONFIG_FILE)
    framework_config = create_bot_config_layout()
    framework_config.parse_file(raw_config_parser, max_index=10)

    manager = SetupManager()
    manager.connect_to_game()
    manager.load_config(framework_config=framework_config,
                        config_location=RLBOT_CONFIG_FILE)
    manager.launch_ball_prediction()
    manager.launch_quick_chat_manager()
    manager.launch_bot_processes()
    manager.start_match()
    manager.infinite_loop()  # Runs terminated by timeout in other thread.
Exemplo n.º 21
0
def stop_match(manager: SetupManager):
    manager.shut_down(kill_all_pids=True, quiet=True)
Exemplo n.º 22
0
def main():

    print("starting")
    manager = SetupManager()
    manager.startup()
    manager.load_config()
    manager.launch_bot_processes()
    manager.run()  # Runs forever until interrupted
    manager.shut_down()
Exemplo n.º 23
0
def start_match_helper(bot_list, match_settings):
    print(bot_list)
    print(match_settings)

    match_config = MatchConfig()
    match_config.game_mode = match_settings['game_mode']
    match_config.game_map = match_settings['map']
    match_config.mutators = MutatorConfig()

    mutators = match_settings['mutators']
    match_config.mutators.match_length = mutators['match_length']
    match_config.mutators.max_score = mutators['max_score']
    match_config.mutators.overtime = mutators['overtime']
    match_config.mutators.series_length = mutators['series_length']
    match_config.mutators.game_speed = mutators['game_speed']
    match_config.mutators.ball_max_speed = mutators['ball_max_speed']
    match_config.mutators.ball_type = mutators['ball_type']
    match_config.mutators.ball_weight = mutators['ball_weight']
    match_config.mutators.ball_size = mutators['ball_size']
    match_config.mutators.ball_bounciness = mutators['ball_bounciness']
    match_config.mutators.boost_amount = mutators['boost_amount']
    match_config.mutators.rumble = mutators['rumble']
    match_config.mutators.boost_strength = mutators['boost_strength']
    match_config.mutators.gravity = mutators['gravity']
    match_config.mutators.demolish = mutators['demolish']
    match_config.mutators.respawn_time = mutators['respawn_time']

    human_index_tracker = IncrementingInteger(0)
    match_config.player_configs = [
        create_player_config(bot, human_index_tracker) for bot in bot_list
    ]

    global sm
    if sm is not None:
        try:
            sm.shut_down()
        except Exception as e:
            print(e)

    sm = SetupManager()
    sm.connect_to_game()
    sm.load_match_config(match_config)
    sm.launch_ball_prediction()
    sm.launch_quick_chat_manager()
    sm.launch_bot_processes()
    sm.start_match()
Exemplo n.º 24
0
def _setup_match(match_config: MatchConfig, manager: SetupManager):
    manager.shut_down(kill_all_pids=True, quiet=True)  # To be safe.
    manager.load_match_config(match_config)
    manager.launch_early_start_bot_processes()
    manager.start_match()
    manager.launch_bot_processes()
    time.sleep(.5)
    manager.try_recieve_agent_metadata()  # Sometimes this launches bots!
Exemplo n.º 25
0
def _setup_match(match_config: MatchConfig, manager: SetupManager):
    manager.shut_down(quiet=True)  # To be safe.
    manager.load_match_config(match_config)
    manager.launch_quick_chat_manager()
    manager.launch_ball_prediction()
    manager.launch_bot_processes()
    manager.start_match()
Exemplo n.º 26
0
class RLBotQTGui(QMainWindow, Ui_MainWindow):
    def __init__(self):
        """
        Creates a new QT mainwindow with the GUI
        """
        super().__init__()
        self.setupUi(self)
        self.overall_config = None
        self.index_manager = IndexManager(MAX_PLAYERS)

        self.agents = []
        self.agent_presets = {}
        self.bot_names_to_agent_dict = {}
        self.loadout_presets = {}
        self.current_bot = None
        self.overall_config_timer = None
        self.setup_manager = None
        self.match_process = None
        self.overall_config = None
        self.overall_config_path = None
        self.launch_in_progress = False

        self.car_customisation = CarCustomisationDialog(self)
        self.agent_customisation = AgentCustomisationDialog(self)
        self.mutator_customisation = MutatorEditor(self)

        if os.path.isfile(DEFAULT_RLBOT_CONFIG_LOCATION):
            self.load_overall_config(DEFAULT_RLBOT_CONFIG_LOCATION)
        else:
            self.load_off_disk_overall_config()

        self.init_match_settings()
        self.update_match_settings()

        self.connect_functions()
        self.update_bot_type_combobox()

    def bot_item_drop_event(self, dropped_listwidget, event):
        """
        Switches the team for the dropped agent to the other team
        :param dropped_listwidget: The listwidget belonging to the new team for the agent
        :param event: The QDropEvent containing the source
        :return:
        """
        dragged_listwidget = event.source()
        if dragged_listwidget is dropped_listwidget:  # drops in the same widget
            return
        self.current_bot.set_team(0 if dropped_listwidget == self.blue_listwidget else 1)
        self.update_teams_listwidgets()

    def fixed_indices(self):
        """
        Agents in the GUI might not have following overall indices,
        thereby a file saved through the GUI would cause other bots to start than the GUI when ran
        :return: CustomConfig instance, copy of the overall config which has the indices sorted out
        """
        config = self.overall_config.copy()

        used_indices = sorted(self.index_manager.numbers)
        not_used_indices = [e for e in range(MAX_PLAYERS) if e not in used_indices]
        order = used_indices + not_used_indices
        header = config[PARTICIPANT_CONFIGURATION_HEADER]
        for name, config_value in header.values.items():
            old_values = list(config_value.value)
            for i in range(MAX_PLAYERS):
                config_value.set_value(old_values[order[i]], index=i)
        return config

    def run_button_pressed(self):
        if self.launch_in_progress:
            # Do nothing if we're already in the process of launching a configuration.
            # Attempting to run again when we're in this state can result in duplicate processes.
            # TODO: Add a mutex around this variable here for safety.
            return
        self.launch_in_progress = True
        if self.setup_manager is not None:
            self.setup_manager.shut_down(time_limit=5, kill_all_pids=False)
            # Leave any external processes alive, e.g. Java or C#, since it can
            # be useful to keep them around. The user can kill them with the
            # Kill Bots button instead.

        self.match_process = threading.Thread(target=self.start_match)
        self.match_process.start()

    def start_match(self):
        """
        Starts a match with the current configuration
        :return:
        """

        agent_configs_dict = {}
        loadout_configs_dict = {}
        for agent in self.agents:
            i, agent_config, loadout_config = agent.get_configs()
            agent_configs_dict[i] = agent_config
            loadout_configs_dict[i] = loadout_config
        agent_configs = {}
        loadout_configs = {}
        index = 0
        for i in range(MAX_PLAYERS):
            if i in agent_configs_dict:
                agent_configs[index] = agent_configs_dict[i]
                loadout_configs[index] = loadout_configs_dict[i]
                index += 1
        self.setup_manager = SetupManager()
        self.setup_manager.load_config(self.overall_config, self.overall_config_path, agent_configs, loadout_configs)
        self.setup_manager.connect_to_game()
        self.setup_manager.launch_ball_prediction()
        self.setup_manager.launch_quick_chat_manager()
        self.setup_manager.launch_bot_processes()
        self.setup_manager.start_match()
        self.launch_in_progress = False
        self.setup_manager.infinite_loop()

    def connect_functions(self):
        """
        Connects all events to the functions which should be called
        :return:
        """
        # Lambda is sometimes used to prevent passing the event parameter.
        self.cfg_load_pushbutton.clicked.connect(lambda: self.load_overall_config())
        self.cfg_save_pushbutton.clicked.connect(lambda: self.save_overall_config())

        self.blue_listwidget.itemSelectionChanged.connect(self.load_selected_bot)
        self.orange_listwidget.itemSelectionChanged.connect(self.load_selected_bot)
        self.blue_listwidget.dropEvent = lambda event: self.bot_item_drop_event(self.blue_listwidget, event)
        self.orange_listwidget.dropEvent = lambda event: self.bot_item_drop_event(self.orange_listwidget, event)

        self.blue_name_lineedit.editingFinished.connect(self.team_settings_edit_event)
        self.orange_name_lineedit.editingFinished.connect(self.team_settings_edit_event)
        self.blue_color_spinbox.valueChanged.connect(self.team_settings_edit_event)
        self.orange_color_spinbox.valueChanged.connect(self.team_settings_edit_event)

        self.blue_minus_toolbutton.clicked.connect(lambda e: self.remove_agent(self.current_bot))
        self.orange_minus_toolbutton.clicked.connect(lambda e: self.remove_agent(self.current_bot))
        self.blue_plus_toolbutton.clicked.connect(lambda e: self.add_agent_button(team_index=0))
        self.orange_plus_toolbutton.clicked.connect(lambda e: self.add_agent_button(team_index=1))

        for child in self.bot_config_groupbox.findChildren(QWidget):
            if isinstance(child, QLineEdit):
                child.editingFinished.connect(self.bot_config_edit_event)
            elif isinstance(child, QSlider):
                child.valueChanged.connect(self.bot_config_edit_event)
            elif isinstance(child, QRadioButton):
                child.toggled.connect(self.bot_config_edit_event)
            elif isinstance(child, QComboBox):
                child.currentTextChanged.connect(self.bot_config_edit_event)
        self.loadout_preset_toolbutton.clicked.connect(self.car_customisation.popup)
        self.agent_preset_toolbutton.clicked.connect(self.agent_customisation.popup)
        self.preset_load_toplevel_pushbutton.clicked.connect(self.load_preset_toplevel)

        for child in self.match_settings_groupbox.findChildren(QWidget):
            if isinstance(child, QComboBox):
                child.currentTextChanged.connect(self.match_settings_edit_event)
            elif isinstance(child, QCheckBox):
                child.toggled.connect(self.match_settings_edit_event)

        self.edit_mutators_pushbutton.clicked.connect(self.mutator_customisation.popup)
        self.kill_bots_pushbutton.clicked.connect(self.kill_bots)
        self.run_button.clicked.connect(self.run_button_pressed)

    def load_preset_toplevel(self):
        preset = self.agent_customisation.load_preset_cfg()
        if preset is None:
            return

        self.agent_preset_combobox.setCurrentText(preset.get_name())

        loadout_preset = self.add_loadout_preset(preset.looks_path)
        self.car_customisation.update_presets_widgets()

        loadout_index = index_of_config_path_in_combobox(self.loadout_preset_combobox, loadout_preset.config_path)
        self.loadout_preset_combobox.setCurrentIndex(loadout_index)

        self.current_bot.set_loadout_preset(loadout_preset)

    def bot_config_edit_event(self, value=None):
        """
        Handles the events called when editing a value regarding the bot configuration
        :param value: the new value to store in the config
        :return:
        """
        sender = self.sender()
        if value is None:
            value = sender.text()
        agent = self.current_bot

        if sender is self.bot_type_combobox:
            self.update_bot_type_combobox()

        elif sender is self.blue_radiobutton and value:  # 'and value' check to make sure that one got selected
            if agent.get_team() != 0:
                agent.set_team(0)
                self.update_teams_listwidgets()
                self.blue_listwidget.setCurrentItem(self.blue_listwidget.findItems(
                    self.validate_name(agent.get_name(), agent), QtCore.Qt.MatchExactly)[0])

        elif sender is self.orange_radiobutton and value:
            if agent.get_team() != 1:
                agent.set_team(1)
                self.update_teams_listwidgets()
                self.orange_listwidget.setCurrentItem(self.orange_listwidget.findItems(
                    self.validate_name(agent.get_name(), agent), QtCore.Qt.MatchExactly)[0])

        elif sender is self.ign_lineedit:
            if agent not in self.agents:
                return
            if not agent.get_team():
                listwidget = self.blue_listwidget
            else:
                listwidget = self.orange_listwidget
            name = self.validate_name(value, agent)
            old_name = self.validate_name(agent.ingame_name, agent)
            row = listwidget.currentRow()
            del self.bot_names_to_agent_dict[old_name]
            agent.set_name(value)
            self.bot_names_to_agent_dict[name] = agent
            self.update_teams_listwidgets()
            listwidget.setCurrentRow(row)
        elif sender is self.loadout_preset_combobox:
            if self.bot_config_groupbox.isEnabled() and self.current_bot is not None:

                index = self.loadout_preset_combobox.currentIndex()
                preset = self.loadout_preset_combobox.itemData(index)

                self.current_bot.set_loadout_preset(preset)
        elif sender is self.agent_preset_combobox:
            if value and self.bot_config_groupbox.isEnabled() and self.current_bot is not None:

                preset = self.agent_preset_combobox.currentData()

                self.current_bot.set_agent_preset(preset)
                agent.set_name(agent.agent_preset.config.get(BOT_CONFIG_MODULE_HEADER, BOT_NAME_KEY))
                self.ign_lineedit.setText(agent.ingame_name)
                if not agent.get_team():
                    listwidget = self.blue_listwidget
                else:
                    listwidget = self.orange_listwidget
                row = listwidget.currentRow()
                self.update_teams_listwidgets()
                listwidget.setCurrentRow(row)

                loadout_index = index_of_config_path_in_combobox(self.loadout_preset_combobox, preset.looks_path)
                if loadout_index is not None:
                    self.loadout_preset_combobox.setCurrentIndex(loadout_index)

        elif sender is self.bot_level_slider:
            agent.set_bot_skill(value / 100)

        if self.cfg_autosave_checkbutton.isChecked() and os.path.isfile(self.overall_config_path):
            self.save_overall_config(10)

    def update_bot_type_combobox(self):
        """
        Handles selecting another bot type in the combobox, hides some frames and shows others depending on the setting
        Also saves the new type if there is a bot selected
        :return:
        """
        bot_type = self.bot_type_combobox.currentText()
        if bot_type == 'RLBot':
            self.rlbot_frame.setHidden(False)
            self.extra_line.setHidden(False)
            self.psyonix_bot_frame.setHidden(True)
            self.appearance_frame.setHidden(False)
            self.label_3.setHidden(False)
            self.ign_lineedit.setHidden(False)
        elif bot_type == 'Psyonix':
            self.psyonix_bot_frame.setHidden(False)
            self.rlbot_frame.setHidden(True)
            self.extra_line.setHidden(False)
            self.appearance_frame.setHidden(False)
            self.label_3.setHidden(False)
            self.ign_lineedit.setHidden(False)
        elif bot_type == 'Human':
            self.psyonix_bot_frame.setHidden(True)
            self.rlbot_frame.setHidden(True)
            self.extra_line.setHidden(True)
            self.appearance_frame.setHidden(False)
            self.label_3.setHidden(True)
            self.ign_lineedit.setHidden(True)
        elif bot_type == 'Party Member Bot':
            self.rlbot_frame.setHidden(False)
            self.extra_line.setHidden(False)
            self.psyonix_bot_frame.setHidden(True)
            self.appearance_frame.setHidden(False)
            self.label_3.setHidden(True)
            self.ign_lineedit.setHidden(True)

        if self.bot_config_groupbox.isEnabled() and self.current_bot is not None:
            config_type = bot_type.lower().replace(" ", "_")
            self.current_bot.set_participant_type(config_type)

    def team_settings_edit_event(self, value=None):
        """
        Handles the events when editing a value regarding the team settings
        :param value: the new value to store in the config
        :return:
        """
        sender = self.sender()
        if value is None:
            value = sender.text()

        if sender is self.blue_name_lineedit:
            self.overall_config.set_value(TEAM_CONFIGURATION_HEADER, "Team Blue Name", value)
        elif sender is self.orange_name_lineedit:
            self.overall_config.set_value(TEAM_CONFIGURATION_HEADER, "Team Orange Name", value)
        elif sender is self.blue_color_spinbox:
            self.overall_config.set_value(TEAM_CONFIGURATION_HEADER, "Team Blue Color", value)
        elif sender is self.orange_color_spinbox:
            self.overall_config.set_value(TEAM_CONFIGURATION_HEADER, "Team Orange Color", value)

        if self.cfg_autosave_checkbutton.isChecked() and os.path.isfile(self.overall_config_path):
            self.save_overall_config(10)

    def update_team_settings(self):
        """
        Sets all team settings widgets to the value in the overall config
        :return:
        """
        self.blue_name_lineedit.setText(self.overall_config.get(TEAM_CONFIGURATION_HEADER, "Team Blue Name"))
        self.orange_name_lineedit.setText(self.overall_config.get(TEAM_CONFIGURATION_HEADER, "Team Orange Name"))
        self.blue_color_spinbox.setValue(self.overall_config.getint(TEAM_CONFIGURATION_HEADER, "Team Blue Color"))
        self.orange_color_spinbox.setValue(self.overall_config.getint(TEAM_CONFIGURATION_HEADER, "Team Orange Color"))

    def load_off_disk_overall_config(self):
        self.cfg_autosave_checkbutton.setChecked(False)
        self.cfg_autosave_checkbutton.setDisabled(True)
        if self.overall_config is None:
            self.overall_config = create_bot_config_layout()
        GUIAgent.overall_config = self.overall_config
        self.overall_config.init_indices(MAX_PLAYERS)
        self.overall_config_path = ""
        self.load_agents()
        # self.load_bot_directory(".")
        self.update_teams_listwidgets()
        if not self.overall_config_path:
            self.cfg_file_path_lineedit.setStyleSheet("border: 1px solid red;")
            self.cfg_file_path_lineedit.setText("Please load a configuration file")
            self.blue_plus_toolbutton.setEnabled(False)
            self.orange_plus_toolbutton.setEnabled(False)
            self.run_button.setEnabled(False)
        else:
            self.cfg_file_path_lineedit.setText(self.overall_config_path)
        self.update_team_settings()
        self.car_customisation.update_presets_widgets()
        self.agent_customisation.update_presets_widgets()
        self.mutator_customisation.update_comboboxes()

    def load_overall_config(self, config_path=None):
        """
        Loads the overall config from the config path, or asks for a path if config_path is None
        :param config_path: the path to load the overall_config from, if None a path is requested
        :return:
        """
        if self.overall_config is None:
            self.overall_config = create_bot_config_layout()
        GUIAgent.overall_config = self.overall_config
        if config_path is None:
            config_path = QFileDialog.getOpenFileName(self, "Load Overall Config", "", "Config Files (*.cfg)")[0]
            if not config_path:
                self.statusbar.showMessage("No file selected, not loading config", 5000)
                return
        if config_path is None or not os.path.isfile(config_path):
            return
        if pathlib.Path(config_path).suffix != '.cfg':
            self.popup_message("This file is not a config file!", "Invalid File Extension", QMessageBox.Warning)
            return
        raw_parser = configparser.RawConfigParser()
        raw_parser.read(config_path, encoding='utf8')
        for section in ['Match Configuration', 'Participant Configuration']:
            if not raw_parser.has_section(section):
                self.popup_message(f"Config file is missing the section {section}, not loading it!",
                                   "Invalid Config File", QMessageBox.Warning)
                return
        self.overall_config_path = config_path
        self.overall_config.parse_file(raw_parser, MAX_PLAYERS, config_directory=os.path.dirname(self.overall_config_path))
        self.load_agents()
        # self.load_bot_directory(".")
        self.update_teams_listwidgets()
        self.cfg_file_path_lineedit.setText(self.overall_config_path)
        self.cfg_file_path_lineedit.setStyleSheet("")
        self.run_button.setEnabled(True)
        self.update_team_settings()
        self.car_customisation.update_presets_widgets()
        self.agent_customisation.update_presets_widgets()
        self.mutator_customisation.update_comboboxes()

    def save_overall_config(self, time_out=0):
        """
        Schedules a save after given time_out
        :param time_out: The amount of seconds it should wait before saving
        :return:
        """
        def save():
            if not os.path.exists(self.overall_config_path):
                return
            self.overall_config_timer.setInterval(1000)
            if self.remaining_save_timer > 0:
                self.statusbar.showMessage("Saving Overall Config in " + str(self.remaining_save_timer) + " seconds")
                self.remaining_save_timer -= 1
            else:
                self.clean_overall_config_loadouts()
                with open(self.overall_config_path, "w", encoding='utf8') as f:
                    f.write(str(self.fixed_indices()))
                self.statusbar.showMessage("Saved Overall Config to " + self.overall_config_path, 5000)
                self.overall_config_timer.stop()
        if self.overall_config_timer is None:
            self.overall_config_timer = QTimer()
            self.overall_config_timer.timeout.connect(save)
        save_path = self.overall_config_path
        if save_path is None or not os.path.isfile(save_path):
            save_path = QFileDialog.getSaveFileName(self, "Save Overall Config", "", "Config Files (*.cfg)")[0]
            if not save_path:
                self.statusbar.showMessage("Unable to save the configuration without a path", 5000)
                return
            self.overall_config_path = save_path
        self.remaining_save_timer = time_out
        self.overall_config_timer.start(0)

    def clean_overall_config_loadouts(self):
        """
        Set all unusued loadout paths to None. This makes sure agents don't have a custom loadout when new agents
        are added in the gui.
        """
        for i in range(MAX_PLAYERS):
            if i not in self.index_manager.numbers:
                self.overall_config.set_value(PARTICIPANT_CONFIGURATION_HEADER, PARTICIPANT_LOADOUT_CONFIG_KEY, "None", i)

    def load_selected_bot(self):
        """
        Loads all the values belonging to the new selected agent into the bot_config_groupbox
        :return:
        """
        # prevent processing from itself (clearing the other one processes this)
        if not self.sender().selectedItems():
            return

        blue = True if self.sender() is self.blue_listwidget else False

        if blue:  # deselect the other listbox
            self.orange_listwidget.clearSelection()
        else:
            self.blue_listwidget.clearSelection()
        item_name = self.sender().selectedItems()[0].text()
        agent = self.bot_names_to_agent_dict[item_name]
        if agent is None:  # something went wrong if agent is None
            return

        self.current_bot = agent

        self.bot_config_groupbox.setEnabled(True)  # Make sure that you can edit the bot
        # enable [-] for right listwidget
        if blue:
            self.blue_minus_toolbutton.setDisabled(False)
            self.orange_minus_toolbutton.setDisabled(True)
        else:
            self.orange_minus_toolbutton.setDisabled(False)
            self.blue_minus_toolbutton.setDisabled(True)

        # load the bot parameters into the edit frame
        agent_type = agent.get_participant_type()

        known_types = ['human', 'psyonix', 'rlbot', 'party_member_bot']
        assert agent_type in known_types, 'Bot has unknown type: %s' % agent_type

        self.bot_type_combobox.setCurrentIndex(known_types.index(agent_type))
        if blue:
            self.blue_radiobutton.setChecked(True)
        else:
            self.orange_radiobutton.setChecked(True)
        self.ign_lineedit.setText(agent.ingame_name)

        loadout_index = index_of_config_path_in_combobox(
            self.loadout_preset_combobox, agent.get_loadout_preset().config_path)
        self.loadout_preset_combobox.setCurrentIndex(loadout_index or 0)

        self.agent_preset_combobox.blockSignals(True)
        self.agent_preset_combobox.setCurrentText(agent.get_agent_preset().get_name())
        self.agent_preset_combobox.blockSignals(False)
        self.bot_level_slider.setValue(int(agent.get_bot_skill() * 100))

    def update_teams_listwidgets(self):
        """
        Clears all items from the listwidgets and then adds everything from the self.agents list again to the right team
        :return:
        """
        self.bot_names_to_agent_dict.clear()
        self.blue_listwidget.clear()
        self.orange_listwidget.clear()
        for agent in self.agents:
            name = self.validate_name(agent.ingame_name, agent)
            if not agent.get_team():
                self.blue_listwidget.addItem(name)
            else:
                self.orange_listwidget.addItem(name)

            self.bot_names_to_agent_dict[name] = agent

        self.enable_disable_on_bot_select_deselect()

        # if max bot count reached: disable + button
        if not self.index_manager.has_free_slots():
            self.blue_plus_toolbutton.setDisabled(True)
            self.orange_plus_toolbutton.setDisabled(True)
        else:
            self.blue_plus_toolbutton.setDisabled(False)
            self.orange_plus_toolbutton.setDisabled(False)

    def enable_disable_on_bot_select_deselect(self):
        """
        Disables the botconfig groupbox and minus buttons when no bot is selected
        :return:
        """
        if not self.blue_listwidget.selectedItems() and not self.orange_listwidget.selectedItems():
            self.bot_config_groupbox.setDisabled(True)
            self.blue_minus_toolbutton.setDisabled(True)
            self.orange_minus_toolbutton.setDisabled(True)
        else:
            self.bot_config_groupbox.setDisabled(False)

    def validate_name(self, name, agent):
        """
        Finds the modification of name which is not yet in the list
        :param name: the (new) name for the agent
        :param agent: the agent instance to allow the same name as the previous one if necessary
        :return: the best modification of name not yet in a listwidget
        """
        if name in self.bot_names_to_agent_dict and self.bot_names_to_agent_dict[name] is not agent:
            i = 0
            while True:
                if name + " (" + str(i) + ")" in self.bot_names_to_agent_dict and \
                        self.bot_names_to_agent_dict[name + " (" + str(i) + ")"] is not agent:
                    i += 1
                else:
                    value = name + " (" + str(i) + ")"
                    return value
        else:
            return name

    def add_agent_button(self, team_index: int):
        """
        The method to handle the [+] button press, adds an agent to the team
        :param team_index: the team to set for the new agent, 0 for blue and 1 for orange
        :return:
        """
        agent = self.load_agent()
        if agent is None:
            return
        agent.set_team(team_index)
        self.car_customisation.update_presets_widgets()
        self.agent_customisation.update_presets_widgets()
        self.update_teams_listwidgets()
        if agent.get_team() == 0:
            self.blue_listwidget.setCurrentItem(self.blue_listwidget.findItems(
                self.validate_name(agent.get_name(), agent), QtCore.Qt.MatchExactly)[0])
        else:
            self.orange_listwidget.setCurrentItem(self.orange_listwidget.findItems(
                self.validate_name(agent.get_name(), agent), QtCore.Qt.MatchExactly)[0])

    def load_agents(self, config_file=None):
        """
        Loads all agents for this team from the rlbot.cfg
        :param config_file:  A config file that is similar to rlbot.cfg
        """
        if config_file is not None:
            self.overall_config = config_file
        self.agents.clear()
        num_participants = get_num_players(self.overall_config)
        try:
            for i in range(num_participants):
                self.load_agent(i)
        except BaseException as e:
            raise ValueError(f"{str(e)}\nPlease check your config files! {self.overall_config_path}")

    def load_agent(self, overall_index: int=None):
        """
        Loads all data for overall_index from the overall config and also loads both presets
        :param overall_index: the index of the targeted agent
        :return agent: an Agent (gui_agent) class with all loaded values
        """
        if not self.index_manager.has_free_slots():
            return None
        if overall_index is None:
            overall_index = self.index_manager.get_new_index()
        else:
            self.index_manager.use_index(overall_index)
        agent = self.add_agent(overall_index=overall_index)

        path_in_overall_config = agent.get_agent_config_path()
        if path_in_overall_config is None:
            # Fall back to the path of the first agent if there's nothing configured.
            path_in_overall_config = self.overall_config.getpath(PARTICIPANT_CONFIGURATION_HEADER,
                                                                 PARTICIPANT_CONFIG_KEY, 0)
        agent_preset = self.add_agent_preset(path_in_overall_config)
        agent.set_agent_preset(agent_preset)
        agent.set_name(agent_preset.config.get(BOT_CONFIG_MODULE_HEADER, BOT_NAME_KEY))

        # Add the preset's loadout as a loadout
        own_loadout = self.add_loadout_preset(agent_preset.looks_path)

        # Agent has a loadout defined in overall config, load that if it is not None
        loadout_file_in_overall_config = self.overall_config.get(PARTICIPANT_CONFIGURATION_HEADER,
                                               PARTICIPANT_LOADOUT_CONFIG_KEY, overall_index)
        if loadout_file_in_overall_config is None or loadout_file_in_overall_config == "None":
            agent.set_loadout_preset(own_loadout)
        else:
            directory = get_python_root()
            file_path = loadout_file_in_overall_config
            loadout_file_in_overall_config = os.path.realpath(os.path.join(directory, file_path))
            loadout_preset = self.add_loadout_preset(loadout_file_in_overall_config)
            agent.set_loadout_preset(loadout_preset)

        return agent

    def load_bot_config_bundle(self, config_bundle: BotConfigBundle):
        self.add_agent_preset(config_bundle.config_path)
        self.add_loadout_preset(config_bundle.looks_path)

    def load_bot_directory(self, directory):
        for bundle in scan_directory_for_bot_configs(directory):
            try:
                self.load_bot_config_bundle(bundle)
            except Exception as e:
                print(e)

    def add_agent(self, overall_index=None, team_index=None):
        """
        Creates the agent using self.agent_class and adds it to the index manager.
        :param overall_index: The index of the bot in the config file if it already exists.
        :param team_index: The index of the team to place the agent in
        :return agent: an Agent (gui_agent) with either given index or a free one, returns None if there is no index given and all indices are occupied
        """
        if overall_index is None:
            if not self.index_manager.has_free_slots():
                return
            overall_index = self.index_manager.get_new_index()
        else:
            self.index_manager.use_index(overall_index)
        agent = GUIAgent(overall_index=overall_index)
        if team_index is not None:
            agent.set_team(team_index)

        self.agents.append(agent)
        self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, PARTICIPANT_COUNT_KEY, len(self.agents))
        return agent

    def remove_agent(self, agent: GUIAgent):
        """
        Removes the given agent.
        :param agent: the agent to remove
        :return:
        """
        self.index_manager.free_index(agent.overall_index)
        self.agents.remove(agent)
        self.update_teams_listwidgets()
        self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, PARTICIPANT_COUNT_KEY, len(self.agents))
        self.overall_config.set_value(PARTICIPANT_CONFIGURATION_HEADER, PARTICIPANT_LOADOUT_CONFIG_KEY, "None", agent.overall_index)
        if len(self.agents) == 0:
            return
        if agent.get_team() == 0:
            if self.blue_listwidget.count() != 0:
                self.blue_listwidget.setCurrentRow(self.blue_listwidget.count() - 1)
            else:
                self.orange_listwidget.setCurrentRow(self.orange_listwidget.count() - 1)
        else:
            if self.orange_listwidget.count() != 0:
                self.orange_listwidget.setCurrentRow(self.orange_listwidget.count() - 1)
            else:
                self.blue_listwidget.setCurrentRow(self.blue_listwidget.count() - 1)

    def add_loadout_preset(self, file_path: str):
        """
        Loads a preset using file_path with all values from that path loaded
        :param file_path: the path to load the preset from, if invalid a default preset is returned
        :return preset: the loadout preset created
        """
        if file_path is not None and os.path.isfile(file_path):
            name = pathlib.Path(file_path).stem
        else:
            name = "new preset"
        if file_path in self.loadout_presets:
            return self.loadout_presets[file_path]
        preset = LoadoutPreset(name, file_path)
        self.loadout_presets[preset.config_path] = preset
        return preset

    def add_agent_preset(self, file_path):
        """
        Loads a preset using file_path with all values from that path loaded
        :param file_path: the path to load the preset from. We'll throw an exception if it's invalid.
        :return preset: the agent preset created
        """
        if file_path is not None and os.path.isfile(file_path):
            name = pathlib.Path(file_path).stem
        else:
            raise FileNotFoundError(f"File path {file_path} is not found!")

        if name in self.agent_presets:
            if self.agent_presets[name].config_path == file_path:
                return self.agent_presets[name]
            else:
                i = 1
                for preset_name in self.agent_presets:
                    if name in preset_name:
                        i += 1
                name = f"{name} ({i})"
        preset = AgentPreset(name, file_path)
        self.agent_presets[preset.get_name()] = preset
        return preset

    def init_match_settings(self):
        """
        Adds all items to the match settings comboboxes
        :return:
        """
        self.mode_type_combobox.addItems(game_mode_types)
        self.map_type_combobox.addItems(map_types)

    def update_match_settings(self):
        """
        Sets all match setting widgets to the values in the overall config
        :return:
        """
        self.mode_type_combobox.setCurrentText(self.overall_config.get(MATCH_CONFIGURATION_HEADER, GAME_MODE))
        self.map_type_combobox.setCurrentText(self.overall_config.get(MATCH_CONFIGURATION_HEADER, GAME_MAP))
        self.skip_replays_checkbox.setChecked(self.overall_config.getboolean(MATCH_CONFIGURATION_HEADER, SKIP_REPLAYS))
        self.instant_start_checkbox.setChecked(
            self.overall_config.getboolean(MATCH_CONFIGURATION_HEADER, INSTANT_START))

    def match_settings_edit_event(self, value):
        """
        Handles all edits to match settings and sets the config value to the new value
        :param value: the value to apply to the overall config
        :return:
        """
        sender = self.sender()

        if sender is self.mode_type_combobox:
            self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, GAME_MODE, value)
        elif sender is self.map_type_combobox:
            self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, GAME_MAP, value)
        elif sender is self.skip_replays_checkbox:
            self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, SKIP_REPLAYS, value)
        elif sender is self.instant_start_checkbox:
            self.overall_config.set_value(MATCH_CONFIGURATION_HEADER, INSTANT_START, value)
        elif sender is self.match_length_combobox:
            self.overall_config.set_value(MUTATOR_CONFIGURATION_HEADER, MUTATOR_MATCH_LENGTH, value)
        elif sender is self.boost_type_combobox:
            self.overall_config.set_value(MUTATOR_CONFIGURATION_HEADER, MUTATOR_BOOST_AMOUNT, value)

        if self.cfg_autosave_checkbutton.isChecked() and os.path.isfile(self.overall_config_path):
            self.save_overall_config(10)

    def popup_message(self, message: str, title: str, icon=QMessageBox.Warning):
        popup = QMessageBox(self)
        popup.setIcon(icon)
        popup.setWindowTitle(title)
        popup.setText(message)
        popup.setStandardButtons(QMessageBox.Ok)
        popup.exec_()

    def kill_bots(self):
        if self.setup_manager is not None:
            self.setup_manager.shut_down(time_limit=5, kill_all_pids=True)
        else:
            print("There gotta be some setup manager already")

    @staticmethod
    def main():
        """
        Start the GUI
        :return:
        """
        app = QApplication(sys.argv)
        rlbot_icon = QtGui.QIcon(os.path.join(get_rlbot_directory(), 'img', 'rlbot_icon.png'))
        app.setWindowIcon(rlbot_icon)
        window = RLBotQTGui()
        window.show()
        app.exec_()
Exemplo n.º 27
0
class PokebotTrainer(BaseScript):

    def __init__(self):
        super().__init__("Pokebot Trainer")
        self.action_broker = MyActionBroker(self)
        self.active_bots: List[ActiveBot] = []
        self.available_bots: Dict[str, BotConfigBundle] = {b.name: b for b in self.get_bots()}
        self.available_bot_names: List[str] = list(self.available_bots.keys())
        self.available_bot_names.sort()
        self.logger.info(f"Pokebot trainer has: {self.available_bots.keys()}")
        self.setup_manager = SetupManager()
        self.ready = False
        self.requested_relaunch: MatchConfig = None
        self.bots_pending_post_spawn_processing: List[ActiveBot] = []

    def index_from_spawn_id(self, spawn_id):
        packet = self.game_tick_packet
        for n in range(0, packet.num_cars):
            packet_spawn_id = packet.game_cars[n].spawn_id
            if spawn_id == packet_spawn_id:
                return n
        return None

    def heartbeat_connection_attempts_to_twitch_broker(self, port):
        register_api_config = Configuration()
        register_api_config.host = f"http://127.0.0.1:{STANDARD_TWITCH_BROKER_PORT}"
        twitch_broker_register = RegisterApi(ApiClient(configuration=register_api_config))
        while True:
            try:
                twitch_broker_register.register_action_server(
                    ActionServerRegistration(base_url=f"http://127.0.0.1:{port}"))
            except MaxRetryError:
                self.logger.warning('Failed to register with twitch broker, will try again...')
            sleep(10)

    def get_bots(self):
        return scan_directory_for_bot_configs(BOT_DIRECTORY)

    def get_actions_currently_available(self) -> List[AvailableActions]:
        actions = []
        for name in self.available_bot_names:
            blue_text = highlight_team_color(f"blue {name}", 0)
            actions.append(BotAction(description=f"Spawn {blue_text}", action_type=SPAWN, data={'name': name, 'team': 0}))
        for name in self.available_bot_names:
            orange_text = highlight_team_color(f"orange {name}", 1)
            actions.append(BotAction(description=f"Spawn {orange_text}", action_type=SPAWN, data={'name': name, 'team': 1}))
        return [AvailableActions(self.name, None, actions)]

    def process_choice(self, choice: BotAction):

        if not self.setup_manager.has_started:
            self.logger.error(f"Tried to {choice.description} before the setup manager was fully started!")
            return

        if choice.action_type == SPAWN:
            name = choice.data['name']
            team = choice.data['team']
            bundle = self.available_bots[name]

            names = set([ab.name for ab in self.active_bots if ab is not None])
            unique_name = name[:31]
            count = 2
            while unique_name in names:
                unique_name = f'{name[:27]} ({count})'  # Truncate at 27 because we can have up to '(10)' appended
                count += 1

            new_bot = ActiveBot(unique_name, team, randint(1, 2**31 - 1), self.game_tick_packet.game_info.seconds_elapsed, bundle)
            try:
                none_index = self.active_bots.index(None)
                self.active_bots[none_index] = new_bot
            except ValueError:
                self.active_bots.append(new_bot)
            self.bots_pending_post_spawn_processing.append(new_bot)
            self.set_pending_relaunch_config(self.active_bots)

    def set_pending_relaunch_config(self, active_bots: List[ActiveBot]):
        match_config = MatchConfig()
        match_config.player_configs = [player_config_from_active_bot(ab) for ab in active_bots]
        match_config.game_mode = 'Soccer'
        match_config.game_map = 'DFHStadium'
        match_config.existing_match_behavior = 'Continue And Spawn'
        match_config.mutators = MutatorConfig()
        self.requested_relaunch = match_config

    def execute_relaunch(self):
        match_config = self.requested_relaunch
        if match_config is None:
            return
        self.requested_relaunch = None
        self.setup_manager.load_match_config(match_config)
        self.setup_manager.start_match()
        self.setup_manager.launch_bot_processes(match_config=match_config)
        self.setup_manager.try_recieve_agent_metadata()
        self.logger.info("Done relaunching bots")
        if GIVE_BOOST:
            car_states = {}
            self.get_game_tick_packet()
            for bot in self.bots_pending_post_spawn_processing:
                index = self.index_from_spawn_id(bot.spawn_id)
                if index is not None:
                    car_states[index] = CarState(boost_amount=100)
            self.set_game_state(GameState(cars=car_states))
        self.bots_pending_post_spawn_processing.clear()

    def start(self):
        port = find_usable_port(9886)
        Thread(target=run_action_server, args=(port,), daemon=True).start()
        set_bot_action_broker(self.action_broker)

        Thread(target=self.heartbeat_connection_attempts_to_twitch_broker, args=(port,), daemon=True).start()

        self.setup_manager.connect_to_game()
        self.ready = True

        while True:
            self.get_game_tick_packet()
            self.setup_manager.try_recieve_agent_metadata()
            game_seconds = self.game_tick_packet.game_info.seconds_elapsed

            needs_relaunch = False
            next_bots: List[ActiveBot] = []
            for b in self.active_bots:
                if b is not None and game_seconds > b.join_time + LIFESPAN:
                    next_bots.append(None)
                    needs_relaunch = True
                else:
                    next_bots.append(b)

            if needs_relaunch:
                while len(next_bots) > 0 and next_bots[-1] is None:
                    next_bots.pop()  # Get rid of any trailing None values.
                self.active_bots = next_bots
                self.set_pending_relaunch_config(next_bots)

            self.execute_relaunch()

            sleep(0.1)
Exemplo n.º 28
0
class TestSpawner:
    def __init__(self, python_file: Path,
                 standalone_bot_config: StandaloneBotConfig,
                 bundle: BotConfigBundle):
        self.python_file = python_file
        self.standalone_bot_config = standalone_bot_config
        self.bundle = bundle
        self.player_config: PlayerConfig = None
        self.setup_manager: SetupManager = None
        self.spawn_id = self.create_spawn_id()
        self.player_index = standalone_bot_config.player_index or 0
        self.team = standalone_bot_config.team or 0
        self.name = self.get_bot_name()

    def get_bot_name(self) -> str:
        if self.bundle is not None:
            return self.bundle.name
        if self.standalone_bot_config.name is not None:
            print(
                f'Spawning your bot with the name {self.standalone_bot_config.name} because no config path was provided!'
            )
            return self.standalone_bot_config.name
        print(
            f'Spawning your bot with the name {self.python_file.name} because no config path was provided!'
        )
        return self.python_file.name

    def create_spawn_id(self):
        """
        We want a spawn id unique to the python file which will be stable across re-runs.
        """
        hash = hashlib.sha1(str(self.python_file).encode('utf-8'))
        number_form = int(hash.hexdigest(), 16)
        return number_form % FLATBUFFER_MAX_INT

    def create_player_config(self, config_path: str) -> PlayerConfig:
        player_config = PlayerConfig()
        player_config.bot = True
        player_config.rlbot_controlled = True
        player_config.bot_skill = 1
        player_config.human_index = 0
        player_config.name = self.name
        player_config.team = 0
        player_config.config_path = config_path
        player_config.spawn_id = self.spawn_id
        return player_config

    def build_match_config(self, player_config_path: str):
        if self.player_config is None:
            self.player_config = self.create_player_config(player_config_path)

        match_config = MatchConfig()
        match_config.player_configs = [self.player_config]
        match_config.game_mode = 'Soccer'
        match_config.game_map = 'DFHStadium'
        match_config.existing_match_behavior = 'Continue And Spawn'
        match_config.mutators = MutatorConfig()
        match_config.enable_state_setting = True
        match_config.enable_rendering = True
        return match_config

    def spawn_bot(self):
        config_path = None
        if self.bundle is not None:
            config_path = self.bundle.config_path

        match_config = self.build_match_config(config_path)

        if self.setup_manager is None:
            self.setup_manager = SetupManager()

            rlbot_gateway_process, _ = gateway_util.find_existing_process()
            if rlbot_gateway_process is None:
                # RLBot.exe is not running yet, we should use the Restart behavior.
                # That avoids a situation where dead cars start piling up when
                # RLBot.exe gets killed and re-launched over and over and lacks context
                # to clean up previous cars.
                match_config.existing_match_behavior = 'Restart'

            self.setup_manager.connect_to_game()

        self.setup_manager.load_match_config(match_config)
        self.setup_manager.start_match()
Exemplo n.º 29
0
def ensure_dll_is_injected():
    manager = SetupManager()
    manager.startup()