hsahovic / poke-env

A python interface for training Reinforcement Learning bots to battle on pokemon showdown
https://poke-env.readthedocs.io/
MIT License
291 stars 98 forks source link

Unexpected halts and Unhandled exception raised while handling message #518

Open matwate opened 5 months ago

matwate commented 5 months ago

This happens in two cases:

  1. Testing Locally A Custom player against a Random Player
  2. Whenever the bot is playing ladder matches online.

Here's the error message:

024-03-22 18:12:03,353 - MatwaAlgorithm 1 - ERROR - Unhandled exception raised while handling message:  
>battle-gen8randombattle-1179
|init|battle
|title|MatwaAlgorithm 1 vs. MatwaAlgorithm 2
|j|☆MatwaAlgorithm 1
|j|☆MatwaAlgorithm 2
|t:|1711149100
|gametype|singles
|player|p1|MatwaAlgorithm 1|170|
|player|p2|MatwaAlgorithm 2|101|
|teamsize|p1|6
|teamsize|p2|6
|gen|8
|tier|[Gen 8] Random Battle
|rule|Species Clause: Limit one of each Pokémon
|rule|HP Percentage Mod: HP is shown in percentages
|rule|Sleep Clause Mod: Limit one foe put to sleep
|rule|Illusion Level Mod: Illusion disguises the Pokémon's true level
|
|t:|1711149100
|start
|switch|p1a: Lugia|Lugia, L73|275/275
|switch|p2a: Drifblim|Drifblim, L84, M|100/100
|turn|1
|
|t:|1711149100
|move|p1a: Lugia|Air Slash|p2a: Drifblim
|-damage|p2a: Drifblim|78/100
|move|p2a: Drifblim|Shadow Ball|p1a: Lugia
|-supereffective|p1a: Lugia
|-damage|p1a: Lugia|217/275
|
|upkeep
|turn|2
|
|t:|1711149100
|move|p1a: Lugia|Air Slash|p2a: Drifblim
|-damage|p2a: Drifblim|55/100
|move|p2a: Drifblim|Shadow Ball|p1a: Lugia
|-supereffective|p1a: Lugia
|-damage|p1a: Lugia|107/275
|
|upkeep
|turn|3
|
|t:|1711149100
|move|p1a: Lugia|Air Slash|p2a: Drifblim
|-damage|p2a: Drifblim|32/100
|-enditem|p2a: Drifblim|Sitrus Berry|[eat]
|-heal|p2a: Drifblim|57/100|[from] item: Sitrus Berry
|cant|p2a: Drifblim|flinch
|
|upkeep
|turn|4
|
|t:|1711149100
|move|p2a: Drifblim|Shadow Ball|p1a: Lugia
|-supereffective|p1a: Lugia
|-damage|p1a: Lugia|0 fnt
|faint|p1a: Lugia
|
|upkeep
|
|t:|1711149100
|switch|p1a: Buzzwole|Buzzwole, L76|288/288
|turn|5
|
|t:|1711149100
|move|p2a: Drifblim|Shadow Ball|p1a: Buzzwole
|-damage|p1a: Buzzwole|170/288
|move|p1a: Buzzwole|Darkest Lariat|p2a: Drifblim
|-supereffective|p2a: Drifblim
|-damage|p2a: Drifblim|3/100
|
|upkeep
|turn|6
|
|t:|1711149100
|move|p2a: Drifblim|Shadow Ball|p1a: Buzzwole
|-damage|p1a: Buzzwole|53/288
|move|p1a: Buzzwole|Darkest Lariat|p2a: Drifblim
|-supereffective|p2a: Drifblim
|-damage|p2a: Drifblim|0 fnt
|faint|p2a: Drifblim
|-ability|p1a: Buzzwole|Beast Boost|boost
|-boost|p1a: Buzzwole|atk|1
|
|upkeep
|
|t:|1711149100
|switch|p2a: Carracosta|Carracosta, L84, F|100/100
|turn|7
|l|☆MatwaAlgorithm 2
|player|p2|
|l|☆MatwaAlgorithm 1
|player|p1|
|j|☆MatwaAlgorithm 2
|player|p2|MatwaAlgorithm 2|101
|j|☆MatwaAlgorithm 1
|player|p1|MatwaAlgorithm 1|170
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p1', '']
Task exception was never retrieved
future: <Task finished name='Task-36' coro=<PSClient._handle_message() done, defined at C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py:124> exception=RuntimeError("Invalid player message: ['', 'player', 'p1', '']")>
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 193, in _handle_message
    raise exception
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p1', '']
Task exception was never retrieved
future: <Task finished name='Task-37' coro=<PSClient._handle_message() done, defined at C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py:124> exception=NotImplementedError(['', 'sentchoice', 'move hydropump'])>
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 193, in _handle_message
    raise exception
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 755, in parse_message
    raise NotImplementedError(split_message)
NotImplementedError: ['', 'sentchoice', 'move hydropump']
Task exception was never retrieved
future: <Task finished name='Task-42' coro=<PSClient._handle_message() done, defined at C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py:124> exception=RuntimeError("Invalid player message: ['', 'player', 'p1', '']")>
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 193, in _handle_message
    raise exception
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p1', '']
Task exception was never retrieved
future: <Task finished name='Task-38' coro=<PSClient._handle_message() done, defined at C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py:124> exception=RuntimeError("Invalid player message: ['', 'player', 'p2', 'MatwaAlgorithm 2', '101']")>
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 193, in _handle_message
    raise exception
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p2', 'MatwaAlgorithm 2', '101']
2024-03-22 18:12:03,371 - MatwaAlgorithm 1 - ERROR - Unhandled exception raised while handling message:  
>battle-gen8randombattle-1179
|player|p1|MatwaAlgorithm 1|170
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p1', 'MatwaAlgorithm 1', '170']
choose_move start
activePoke: buzzwole (pokemon object) [Active: True, Status: None]
formula calculated
closecombat (Move object)
Function 'choose_move' executed in 0.0000s
2024-03-22 18:12:03,378 - MatwaAlgorithm 2 - ERROR - Unhandled exception raised while handling message:
>battle-gen8randombattle-1179
|player|p1|MatwaAlgorithm 1|170
Traceback (most recent call last):
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\ps_client\ps_client.py", line 138, in _handle_message
    await self._handle_battle_message(split_messages)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\player\player.py", line 353, in _handle_battle_message
    battle.parse_message(split_message)
  File "C:\Users\mateo\AppData\Local\Programs\Python\Python312\Lib\site-packages\poke_env\environment\abstract_battle.py", line 686, in parse_message
    raise RuntimeError(f"Invalid player message: {split_message}")
RuntimeError: Invalid player message: ['', 'player', 'p1', 'MatwaAlgorithm 1', '170']

My custom player code is the following:

class MatwaAlgorithm(Player):

    def switch(self, battle):
        print("switch start")
        if battle.available_switches:
            print("switch end")
            poke = battle.available_switches[random.randint(0, len(battle.available_switches) - 1)]
            return self.create_order(poke)
        return self.choose_random_move(battle)
    @timer
    def choose_move(self, battle):
        try:
            print("choose_move start")
            if battle.available_moves:
                activePoke = battle.active_pokemon
                print(f'activePoke: {activePoke}')
                bestMove = utils.getBestMove(
                    self= utils,
                    pokemon=activePoke,
                    type1=battle.opponent_active_pokemon.type_1,
                    type2=battle.opponent_active_pokemon.type_2,
                    stabType=
                    [activePoke.type_1, activePoke.type_2]

                )
                if utils.damageFormula(
                    utils,
                    bestMove,
                    activePoke,
                    battle.opponent_active_pokemon.type_1,
                    battle.opponent_active_pokemon.type_2
                )> battle.opponent_active_pokemon.current_hp * 0.25:
                    print(bestMove)
                    return self.create_order(bestMove)

                else:
                    print(f'battle: {battle}')

                    return self.switch(battle)
            else:

                return self.switch(battle)    
        except Exception as e:
             print(f"Error in choose_move: {e}")

and the functions from "utils"

class utils():
    def __init__(self):
        pass

    def getExpectedDamage(self, move: Move):
        return move.base_power * move.accuracy

    def get_multiplier_with_stab(self, move: Move, pokemon_types: list[PokemonType], type1: PokemonType, type2: PokemonType):
        return move.type.damage_multiplier(type1, type2, type_chart=type_chart) * 1.5 if (move.type in pokemon_types) else 1

    def getExpectedPowerWithStab(self, move: Move, stabType:list[PokemonType], type1:PokemonType, type2:PokemonType):
        return self.getExpectedDamage(self, move) * self.get_multiplier_with_stab(self, move, stabType, type1, type2) 

    def damageFormula(self, move: Move, pokemon: Pokemon, type1: PokemonType, type2: PokemonType):
        print("formula calculated")
        return (((200/5 + 2) * move.base_power) / 50 + 2) *  self.get_multiplier_with_stab(self, move, [pokemon.type_1, pokemon.type_2], type1, type2)

    def getMoveScore(self,move: Move, type1: PokemonType, type2: PokemonType, stabType:list[PokemonType]):    
        return  self.getExpectedPowerWithStab(self, move, stabType, type1, type2)

    def getBestMove(self, pokemon:Pokemon, type1: PokemonType , type2: PokemonType, stabType:list[PokemonType]):
        max_score = -1
        best_move = None
        for move in pokemon.moves.values():
            try:
                score =  self.getMoveScore(self,move= move, type1= type1, type2= type2, stabType= stabType)
                if score > max_score:
                    max_score = score
                    best_move = move
            except Exception as e:
                print(f"Error calculating score for move {move}: {e}")

        if best_move is None:
            print("No best move found, defaulting to first move")
            best_move = pokemon.moves[0]

        return best_move
matwate commented 5 months ago

Even weirder is, sometimes the bot just stops, no clue why and no steps to reproduce as its seemingly random

JotaDeRodriguez commented 2 months ago

Hey! Im having the exact same issue. Did you find the solution or any leads?

JotaDeRodriguez commented 2 months ago

Take a look at this comment

Might help.

matwate commented 2 months ago

Take a look at this comment

Might help.

Idk if the issue you mentioned is going to fix it 100%, since i think its not returning None anywhere, maybe i have to choose a random move in the outer most except block?

Microgorath commented 1 month ago

For my code, the sentchoice NotImplementedError occurred when players have the same usernames as those in previous unfinished battles. For some reason they rejoin these old battle rooms and battle in them. I do not know why they do this. The rejoining previous rooms happens fairly regularly, but I believe the error only occurs when the room is deemed inactive. The player tries to send an action choice for the inactive battle, which results in the error. This rejoining old rooms also persists across runs, unless the Showdown server is reset. I am currently looking into why. It probably has to do with the challenge loop, but I don't understand how they can apparently accept the same challenge for a room more than once, even across runs.

As for the other RunTimeErrors, I have not seen any of them.

Microgorath commented 1 month ago

I found what was causing the sentchoice NotImplementedError. You need to call .close() on the gym environment when done using it, always. If your run crashes or otherwise ends before calling .close() on all gym environments you started, it will leave all unfinished battles open, with no easy way of closing them. This results in my previously mentioned behavior, where players with the same usernames as those in the unfinished battle will rejoin the unfinished battle and cause the sentchoice NotImplementedError. In the case where the run crashes or ends before calling .close() on all gym environments, the only solution I could find was to restart the local showdown server. I tested adding "sentchoice" to AbstractBattle's MESSAGES_TO_IGNORE, but that resulted in occasional hangs on later environment .reset() calls and an exception on .close(). The old unfinished battle seems to cause an offset where an extra battle is created but not added into the environment's dictionary of battles.

Xemorr commented 1 month ago

I found what was causing the sentchoice NotImplementedError. You need to call .close() on the gym environment when done using it, always. If your run crashes or otherwise ends before calling .close() on all gym environments you started, it will leave all unfinished battles open, with no easy way of closing them. This results in my previously mentioned behavior, where players with the same usernames as those in the unfinished battle will rejoin the unfinished battle and cause the sentchoice NotImplementedError. In the case where the run crashes or ends before calling .close() on all gym environments, the only solution I could find was to restart the local showdown server. I tested adding "sentchoice" to AbstractBattle's MESSAGES_TO_IGNORE, but that resulted in occasional hangs on later environment .reset() calls and an exception on .close(). The old unfinished battle seems to cause an offset where an extra battle is created but not added into the environment's dictionary of battles.

I'm not using a gym environment but experiencing this issue, is there any way of just cleaning up all battles when turning the bot back on?

Microgorath commented 1 month ago

I'm not using a gym environment but experiencing this issue, is there any way of just cleaning up all battles when turning the bot back on?

Not that I know of, but I'm working on a solution right now.

I think an if statement may be added to Player's _handle_battle_message before it creates a battle object, after line 255. This should ignore battle object creation if the battle was created before the Player, using the UNIX timestamp |t:| provided by showdown in the battle's initialization message. This way the player never recognizes battles made before it.