CLAUDE: Fix gauntlet game creation and Event 9 issues
Multiple fixes to resolve PlayNotFoundException and lineup initialization errors: 1. gauntlets.py: - Fixed Team object subscriptable errors (use .id instead of ['id']) - Added fallback cardsets (24, 25, 26) for Event 9 RP shortage - Fixed draft_team type handling (can be Team object or dict) 2. cogs/gameplay.py: - Fixed gauntlet game creation flow to read field player lineup from sheets - Catches LineupsMissingException when SP not yet selected - Instructs user to run /gamestate after SP selection 3. utilities/dropdown.py: - Fixed SelectStartingPitcher to create own session instead of using closed session - Store game/team IDs instead of objects to avoid detached session issues - Added exception handling for failed legality check API calls These changes fix the issue where gauntlet games would fail to initialize because the SP lineup entry wasn't being committed to the database. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
07195f9ad3
commit
c043948238
@ -656,14 +656,11 @@ class Gameplay(commands.Cog):
|
||||
confirmation_message='Got it!'
|
||||
)
|
||||
|
||||
sp_view = starting_pitcher_dropdown_view(session, this_game, human_team, this_game.league_name, [interaction.user])
|
||||
await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view)
|
||||
|
||||
# Read the 9 field players from sheets (this will fail to initialize play without SP)
|
||||
try:
|
||||
await asyncio.sleep(5)
|
||||
this_play = await read_lineup(
|
||||
session,
|
||||
interaction,
|
||||
await read_lineup(
|
||||
session,
|
||||
interaction,
|
||||
this_game=this_game,
|
||||
lineup_team=human_team,
|
||||
sheets_auth=self.sheets,
|
||||
@ -671,16 +668,17 @@ class Gameplay(commands.Cog):
|
||||
league_name=this_game.game_type
|
||||
)
|
||||
except LineupsMissingException as e:
|
||||
logger.error(f'Attempting to start game, pausing for 5 seconds: {e}')
|
||||
await asyncio.sleep(5)
|
||||
|
||||
try:
|
||||
this_play = this_game.current_play_or_none(session)
|
||||
await self.post_play(session, interaction, this_play, buffer_message='Game on!')
|
||||
except LineupsMissingException as e:
|
||||
await interaction.channel.send(
|
||||
content=f'Run `/gamestate` once you have selected a Starting Pitcher to get going!'
|
||||
)
|
||||
# Expected - can't initialize play without SP yet
|
||||
logger.info(f'Field player lineup read from sheets, waiting for SP selection: {e}')
|
||||
|
||||
sp_view = starting_pitcher_dropdown_view(session, this_game, human_team, this_game.league_name, [interaction.user])
|
||||
await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view)
|
||||
|
||||
# Don't try to initialize play immediately - wait for user to select SP
|
||||
# The play will be initialized when they run /gamestate
|
||||
await interaction.channel.send(
|
||||
content=f'Once you\'ve selected your Starting Pitcher, run `/gamestate` to get the game started!'
|
||||
)
|
||||
|
||||
@group_new_game.command(name='exhibition', description='Start a new custom game against an AI')
|
||||
@app_commands.choices(
|
||||
|
||||
22
gauntlets.py
22
gauntlets.py
@ -423,9 +423,12 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even
|
||||
if this_event['id'] in [1, 2]:
|
||||
max_counts['MVP'] = 2
|
||||
elif this_event['id'] in [5, 6, 8, 9]:
|
||||
# Handle draft_team as either Team object or dict
|
||||
dt_season = draft_team.season if isinstance(draft_team, Team) else draft_team['season']
|
||||
dt_id = draft_team.id if isinstance(draft_team, Team) else draft_team['id']
|
||||
g_query = await db_get(
|
||||
'games',
|
||||
params=[('season', draft_team.season), ('team1_id', draft_team.id), ('gauntlet_id', this_event['id'])]
|
||||
params=[('season', dt_season), ('team1_id', dt_id), ('gauntlet_id', this_event['id'])]
|
||||
)
|
||||
if g_query['count'] > 4:
|
||||
game_count = g_query['count']
|
||||
@ -779,6 +782,14 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even
|
||||
slot_params.extend(params)
|
||||
p_query = await db_get('players/random', params=slot_params)
|
||||
|
||||
# Fallback for Event 9 RP shortage
|
||||
if this_event['id'] == 9 and x == 'RP' and p_query['count'] < 3:
|
||||
logger.warning(f'Low RP count ({p_query["count"]}) in Event 9, expanding cardsets to 24, 25, 26')
|
||||
fallback_params = [p for p in slot_params if p[0] != 'cardset_id']
|
||||
fallback_params.extend([('cardset_id', 24), ('cardset_id', 25), ('cardset_id', 26)])
|
||||
p_query = await db_get('players/random', params=fallback_params)
|
||||
logger.info(f'Fallback query returned {p_query["count"]} RP options')
|
||||
|
||||
if p_query['count'] > 0:
|
||||
# test_player_list = ''
|
||||
# for z in p_query['players']:
|
||||
@ -1720,10 +1731,13 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even
|
||||
raise KeyError(f'I gotta be honest - I shit the bed here and wasn\'t able to get you enough players to fill '
|
||||
f'a team. I have to wipe this team, but please draft again after you tell Cal his bot sucks.')
|
||||
|
||||
# Handle draft_team as either Team object or dict
|
||||
draft_team_id = draft_team.id if isinstance(draft_team, Team) else draft_team['id']
|
||||
|
||||
this_pack = await db_post(
|
||||
'packs/one',
|
||||
payload={
|
||||
'team_id': draft_team.id,
|
||||
'team_id': draft_team_id,
|
||||
'pack_type_id': 2,
|
||||
'open_time': datetime.datetime.timestamp(datetime.datetime.now()) * 1000
|
||||
}
|
||||
@ -1731,13 +1745,13 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even
|
||||
await db_post(
|
||||
'cards',
|
||||
payload={'cards': [
|
||||
{'player_id': x['player_id'], 'team_id': draft_team.id, 'pack_id': this_pack['id']} for x in all_players
|
||||
{'player_id': x['player_id'], 'team_id': draft_team_id, 'pack_id': this_pack['id']} for x in all_players
|
||||
]}
|
||||
)
|
||||
await db_post(
|
||||
'gauntletruns',
|
||||
payload={
|
||||
'team_id': draft_team.id,
|
||||
'team_id': draft_team_id,
|
||||
'gauntlet_id': this_event['id']
|
||||
}
|
||||
)
|
||||
|
||||
@ -110,13 +110,13 @@ class SelectViewDefense(discord.ui.Select):
|
||||
class SelectStartingPitcher(discord.ui.Select):
|
||||
def __init__(self, this_game: Game, this_team: Team, session: Session, league_name: str, custom_id: str = MISSING, placeholder: str | None = None, options: List[SelectOption] = ..., responders: list[discord.User] = None) -> None:
|
||||
logger.info(f'Inside SelectStartingPitcher init function')
|
||||
self.game = this_game
|
||||
self.team = this_team
|
||||
self.session = session
|
||||
# Store IDs instead of objects to avoid session issues
|
||||
self.game_id = this_game.id
|
||||
self.team_id = this_team.id
|
||||
self.league_name = league_name
|
||||
self.responders = responders
|
||||
super().__init__(custom_id=custom_id, placeholder=placeholder, options=options)
|
||||
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
if self.responders is not None and interaction.user not in self.responders:
|
||||
await interaction.response.send_message(
|
||||
@ -127,52 +127,61 @@ class SelectStartingPitcher(discord.ui.Select):
|
||||
await interaction.response.defer(thinking=True)
|
||||
logger.info(f'SelectStartingPitcher - selection: {self.values[0]}')
|
||||
|
||||
# Get Human SP card
|
||||
human_sp_card = await get_card_or_none(self.session, card_id=self.values[0])
|
||||
if human_sp_card is None:
|
||||
log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found')
|
||||
# Create a new session for this callback
|
||||
from in_game.gameplay_models import engine
|
||||
with Session(engine) as session:
|
||||
# Get fresh game and team objects
|
||||
this_game = session.get(Game, self.game_id)
|
||||
this_team = session.get(Team, self.team_id)
|
||||
|
||||
if human_sp_card.team_id != self.team.id:
|
||||
logger.error(f'Card_id {self.values[0]} does not belong to {self.team.abbrev} in Game {self.game.id}')
|
||||
await interaction.channel.send(
|
||||
f'Uh oh. Card ID {self.values[0]} is {human_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?'
|
||||
)
|
||||
return
|
||||
# Get Human SP card
|
||||
human_sp_card = await get_card_or_none(session, card_id=self.values[0])
|
||||
if human_sp_card is None:
|
||||
log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found')
|
||||
|
||||
await get_position(self.session, human_sp_card, 'P')
|
||||
|
||||
try:
|
||||
legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
|
||||
if not legal_data['legal']:
|
||||
await interaction.edit_original_response(
|
||||
content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {self.league_name} games. You can start a new game once you pick a new SP.'
|
||||
if human_sp_card.team_id != this_team.id:
|
||||
logger.error(f'Card_id {self.values[0]} does not belong to {this_team.abbrev} in Game {this_game.id}')
|
||||
await interaction.channel.send(
|
||||
f'Uh oh. Card ID {self.values[0]} is {human_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?'
|
||||
)
|
||||
return
|
||||
except LegalityCheckNotRequired:
|
||||
pass
|
||||
return
|
||||
|
||||
human_sp_lineup = Lineup(
|
||||
team_id=self.team.id,
|
||||
player_id=human_sp_card.player.id,
|
||||
card_id=self.values[0],
|
||||
position='P',
|
||||
batting_order=10,
|
||||
is_fatigued=False,
|
||||
game=self.game
|
||||
)
|
||||
self.session.add(human_sp_lineup)
|
||||
self.session.commit()
|
||||
await get_position(session, human_sp_card, 'P')
|
||||
|
||||
logger.info(f'trying to delete interaction: {interaction}')
|
||||
try:
|
||||
# await interaction.delete_original_response()
|
||||
await interaction.edit_original_response(
|
||||
# content=f'The {self.team.lname} are starting **{human_sp_card.player.name_with_desc}**!\n\nRun `/set lineup` to import your lineup and `/gamestate` if you are ready to play.',
|
||||
content=f'The {self.team.lname} are starting **{human_sp_card.player.name_with_desc}**!',
|
||||
view=None
|
||||
try:
|
||||
legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
|
||||
if not legal_data['legal']:
|
||||
await interaction.edit_original_response(
|
||||
content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {self.league_name} games. You can start a new game once you pick a new SP.'
|
||||
)
|
||||
return
|
||||
except (LegalityCheckNotRequired, Exception) as e:
|
||||
# Skip legality check if not required or if it fails
|
||||
logger.info(f'Skipping legality check: {e}')
|
||||
pass
|
||||
|
||||
human_sp_lineup = Lineup(
|
||||
team_id=this_team.id,
|
||||
player_id=human_sp_card.player.id,
|
||||
card_id=self.values[0],
|
||||
position='P',
|
||||
batting_order=10,
|
||||
is_fatigued=False,
|
||||
game=this_game
|
||||
)
|
||||
except Exception as e:
|
||||
log_exception(e, 'Couldn\'t clean up after selecting sp')
|
||||
session.add(human_sp_lineup)
|
||||
session.commit()
|
||||
|
||||
logger.info(f'trying to delete interaction: {interaction}')
|
||||
try:
|
||||
# await interaction.delete_original_response()
|
||||
await interaction.edit_original_response(
|
||||
# content=f'The {this_team.lname} are starting **{human_sp_card.player.name_with_desc}**!\n\nRun `/set lineup` to import your lineup and `/gamestate` if you are ready to play.',
|
||||
content=f'The {this_team.lname} are starting **{human_sp_card.player.name_with_desc}**!',
|
||||
view=None
|
||||
)
|
||||
except Exception as e:
|
||||
log_exception(e, 'Couldn\'t clean up after selecting sp')
|
||||
|
||||
|
||||
class SelectReliefPitcher(discord.ui.Select):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user