From e23a7ecf2e85f75c5677709b2012e087067652e2 Mon Sep 17 00:00:00 2001 From: nhaar <38634785+nhaar@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:16:52 -0300 Subject: [PATCH] Improve stamps end screen (#101) * properly handle players exiting cardjitsu water * make end screens only show stamps from current session * add card-jitsu end game screen * fix oversight with how water players get removed * fix card jitsu fire stamp awards * change session stamps to use redis --- houdini/handlers/games/__init__.py | 49 +++++++++------ houdini/handlers/games/ninja/card.py | 73 +++++++++++----------- houdini/handlers/games/ninja/fire.py | 32 ++++++---- houdini/handlers/games/ninja/water.py | 74 ++++++++++++++++------ houdini/penguin.py | 89 +++++++++++++++++++++++++++ 5 files changed, 234 insertions(+), 83 deletions(-) diff --git a/houdini/handlers/games/__init__.py b/houdini/handlers/games/__init__.py index ae181b0..8d281dc 100644 --- a/houdini/handlers/games/__init__.py +++ b/houdini/handlers/games/__init__.py @@ -78,30 +78,34 @@ async def handle_get_game_over(p, score: int): if p.room.id == 996: return - if p.room.game and not p.waddle and not p.table: + # card-jitsus except snow have special handling + card_jitsu_rooms = [995, 998, 997] + is_card_jitsu = p.room.id in card_jitsu_rooms + + # Waddle minigames don't normally need the end screen + if p.waddle and not is_card_jitsu: + return + + if p.room.game and not p.table: coins_earned = determine_coins_earned(p, score) - if await determine_coins_overdose(p, coins_earned): - return await cheat_ban(p, p.id, comment='Coins overdose') - collected_stamps_string, total_collected_stamps, total_game_stamps, total_stamps = '', 0, 0, 0 + if not is_card_jitsu: + if await determine_coins_overdose(p, coins_earned): + return await cheat_ban(p, p.id, comment="Coins overdose") + + stamp_info = "", 0, 0, 0 + if p.room.stamp_group: - game_stamps = [stamp for stamp in p.server.stamps.values() if stamp.group_id == p.room.stamp_group] - collected_stamps = [stamp for stamp in game_stamps if stamp.id in p.stamps] - - total_stamps = len([stamp for stamp in p.stamps.values() if p.server.stamps[stamp.stamp_id].group_id]) - total_collected_stamps = len(collected_stamps) - total_game_stamps = len(game_stamps) - collected_stamps_string = '|'.join(str(stamp.id) for stamp in collected_stamps) - - if total_collected_stamps == total_game_stamps: + stamp_info = await p.get_game_end_stamps_info(True) + # has all stamps in game + if stamp_info[1] == stamp_info[2]: coins_earned *= 2 - await p.update(coins=min(p.coins + coins_earned, p.server.config.max_coins)).apply() - await p.send_xt('zo', p.coins, - collected_stamps_string, - total_collected_stamps, - total_game_stamps, - total_stamps) + if not is_card_jitsu: + await p.update( + coins=min(p.coins + coins_earned, p.server.config.max_coins) + ).apply() + await p.send_xt("zo", p.coins, *stamp_info) @handlers.handler(XTPacket('ggd', ext='z'), client=ClientType.Vanilla) @@ -146,3 +150,10 @@ async def handle_game_complete(p, medals: int): medals = min(6, medals) await p.update(career_medals=p.career_medals + medals, agent_medals=p.agent_medals + medals).apply() + + +@handlers.disconnected +@handlers.player_attribute(joined_world=True) +async def clear_stamp_sessions(p): + """When disconnected, clear stamps in case any were obtained and not properly handled""" + await p.clear_stamps_session() diff --git a/houdini/handlers/games/ninja/card.py b/houdini/handlers/games/ninja/card.py index 42f1d09..d307643 100644 --- a/houdini/handlers/games/ninja/card.py +++ b/houdini/handlers/games/ninja/card.py @@ -1,6 +1,7 @@ import itertools import math import random +import enum from collections import Counter from dataclasses import dataclass from typing import Dict, List, Union @@ -10,6 +11,19 @@ from houdini.data.ninja import Card from houdini.handlers import XTPacket from houdini.penguin import Penguin +class CardStamp(enum.IntEnum): + """ID of Card-Jitsu stamps""" + GRASSHOPPER = 230 + ELEMENTAL_WIN = 242 + FINE_STUDENT = 232 + FLAWLESS_VICTORY = 238 + ONE_ELEMENT = 244 + TRUE_NINJA = 234 + MATCH_MASTER = 240 + NINJA_MASTER = 236 + SENSEI_CARD = 246 + FULL_DOJO = 248 + @dataclass class Played: @@ -44,7 +58,12 @@ class CardJitsuLogic(IWaddle): ItemAwards = [4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 104] PostcardAwards = {0: 177, 4: 178, 8: 179} - StampAwards = {0: 230, 4: 232, 8: 234, 9: 236} + StampAwards = { + 0: CardStamp.GRASSHOPPER, + 4: CardStamp.FINE_STUDENT, + 8: CardStamp.TRUE_NINJA, + 9: CardStamp.NINJA_MASTER, + } StampGroupId = 38 def __init__(self, waddle): @@ -235,7 +254,7 @@ async def ninja_rank_up(p, ranks=1): if rank in CardJitsuLogic.PostcardAwards: await p.add_inbox(p.server.postcards[CardJitsuLogic.PostcardAwards[rank]]) if rank in CardJitsuLogic.StampAwards: - await p.add_stamp(p.server.stamps[CardJitsuLogic.StampAwards[rank]]) + await p.add_card_jitsu_stamp(CardJitsuLogic.StampAwards[rank]) await p.update(ninja_rank=p.ninja_rank + ranks).apply() return True @@ -266,20 +285,10 @@ async def ninja_progress(p, won=False): await ninja_rank_up(p) await p.send_xt('cza', p.ninja_rank) -async def ninja_stamps_earned(p): - game_stamps = [stamp for stamp in p.server.stamps.values() if stamp.group_id == p.room.stamp_group] - collected_stamps = [stamp for stamp in game_stamps if stamp.id in p.stamps] - total_collected_stamps = len(collected_stamps) - total_game_stamps = len(game_stamps) - collected_stamps_string = '|'.join(str(stamp.id) for stamp in collected_stamps) - await p.send_xt('cjsi', collected_stamps_string, total_collected_stamps, total_game_stamps) - async def ninja_win(winner, loser): await ninja_progress(winner.penguin, won=True) await ninja_progress(loser.penguin, won=False) - await ninja_stamps_earned(winner.penguin) - await ninja_stamps_earned(loser.penguin) await winner.penguin.waddle.remove_penguin(winner.penguin) await loser.penguin.waddle.remove_penguin(loser.penguin) @@ -304,6 +313,8 @@ async def handle_update_game(p): @handlers.handler(XTPacket('lz', ext='z')) @handlers.waddle(CardJitsuLogic, CardJitsuMatLogic, SenseiLogic) async def handle_leave_game(p): + # sending stamp info fixes the game taking a bit to close when quitting + await p.send_card_jitsu_stamp_info() seat_id = p.waddle.get_seat_id(p) await p.waddle.send_xt('cz', p.safe_name, f=lambda penguin: penguin is not p) await p.waddle.send_xt('lz', seat_id, f=lambda penguin: penguin is not p) @@ -357,9 +368,8 @@ async def handle_send_pick(p, action: str, card_id: int): winner_seat_id = p.waddle.get_round_winner() if me.chosen.card.id == 256 or opponent.chosen.card.id == 256: - stamp = p.server.stamps[246] - await me.penguin.add_stamp(stamp, notify=True) - await opponent.penguin.add_stamp(stamp, notify=True) + await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD) + await opponent.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD) if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed: await p.waddle.send_xt('zm', 'power', seat_id, opponent_seat_id, me.chosen.card.power_id) @@ -383,19 +393,18 @@ async def handle_send_pick(p, action: str, card_id: int): if winning_cards: await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards)) - stamp = p.server.stamps[[244, 242][win_method]] - await winner.penguin.add_stamp(stamp, notify=True) + stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method] + await winner.penguin.add_card_jitsu_stamp(stamp) if all(not cards for cards in loser.bank.values()): - stamp = p.server.stamps[238] - await winner.penguin.add_stamp(stamp, notify=True) + await winner.penguin.add_card_jitsu_stamp( + CardStamp.FLAWLESS_VICTORY + ) if sum(1 for cards in winner.bank.values() for _ in cards) >= 9: - stamp = p.server.stamps[248] - await winner.penguin.add_stamp(stamp, notify=True) + await winner.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO) await winner.penguin.update(ninja_matches_won=winner.penguin.ninja_matches_won+1).apply() if winner.penguin.ninja_matches_won == 25: - stamp = p.server.stamps[240] - await winner.penguin.add_stamp(stamp, notify=True) + await winner.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER) await ninja_win(winner, loser) else: @@ -493,8 +502,7 @@ async def handle_send_sensei_pick(p, action: str, card_id: int): winner_seat_id = p.waddle.get_round_winner() if me.chosen.card.id == 256 or sensei.chosen.card.id == 256: - stamp = p.server.stamps[246] - await p.add_stamp(stamp, notify=True) + await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD) if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed: await p.send_xt('zm', 'power', 1, 0, me.chosen.card.power_id) @@ -518,21 +526,17 @@ async def handle_send_sensei_pick(p, action: str, card_id: int): await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards)) if winner == me: - stamp = p.server.stamps[[244, 242][win_method]] - await p.add_stamp(stamp, notify=True) + stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method] + await me.penguin.add_card_jitsu_stamp(stamp) if all(not cards for cards in sensei.bank.values()): - stamp = p.server.stamps[238] - await p.add_stamp(stamp, notify=True) + await me.penguin.add_card_jitsu_stamp(CardStamp.FLAWLESS_VICTORY) if sum(1 for cards in me.bank.values() for _ in cards) >= 9: - stamp = p.server.stamps[248] - await p.add_stamp(stamp, notify=True) + await me.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO) await p.update(ninja_matches_won=p.ninja_matches_won + 1).apply() if p.ninja_matches_won == 25: - stamp = p.server.stamps[240] - await p.add_stamp(stamp, notify=True) + await me.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER) - await ninja_stamps_earned(p) can_rank_up = await ninja_rank_up(p) if can_rank_up: await p.send_xt('cza', p.ninja_rank) @@ -548,7 +552,6 @@ async def handle_send_sensei_pick(p, action: str, card_id: int): if can_rank_up: await p.send_xt('cza', p.ninja_rank) await p.waddle.send_xt('czo', 0, winner_seat_id) - await ninja_stamps_earned(p) await p.send_xt('zm', 'judge', winner_seat_id) me.chosen = None diff --git a/houdini/handlers/games/ninja/fire.py b/houdini/handlers/games/ninja/fire.py index a4041e4..89375cb 100644 --- a/houdini/handlers/games/ninja/fire.py +++ b/houdini/handlers/games/ninja/fire.py @@ -2,6 +2,7 @@ import asyncio import itertools import math import random +import enum from collections import Counter from dataclasses import dataclass, field from typing import List, Union @@ -9,9 +10,18 @@ from typing import List, Union from houdini import IWaddle, handlers from houdini.data.ninja import Card from houdini.handlers import XTPacket -from houdini.handlers.games.ninja.card import ninja_stamps_earned from houdini.penguin import Penguin +class FireStamp(enum.IntEnum): + """IDs of Card-Jitsu Fire stamps""" + WARM_UP = 252 + SCORE_FIRE = 254 + FIRE_MIDWAY = 256 + STRONG_DEFENCE = 260 + FIRE_SUIT = 262 + FIRE_NINJA = 264 + MAX_ENERGY = 266 + FIRE_EXPERT = 268 @dataclass class FireNinja: @@ -37,7 +47,7 @@ class CardJitsuFireLogic(IWaddle): AutoBattleTimeout = 22 ItemAwards = [6025, 4120, 2013, 1086, 3032] - StampAwards = {2: 256, 4: 262} + StampAwards = {1: FireStamp.FIRE_MIDWAY, 3: FireStamp.FIRE_SUIT} def __init__(self, waddle): super().__init__(waddle) @@ -524,7 +534,7 @@ async def fire_ninja_rank_up(p, ranks=1): for rank in range(p.fire_ninja_rank, p.fire_ninja_rank+ranks): await p.add_inventory(p.server.items[CardJitsuFireLogic.ItemAwards[rank]], notify=False) if rank in CardJitsuFireLogic.StampAwards: - await p.add_stamp(p.server.stamps[CardJitsuFireLogic.StampAwards[rank]]) + await p.add_card_jitsu_stamp(CardJitsuFireLogic.StampAwards[rank]) await p.update( fire_ninja_rank=p.fire_ninja_rank + ranks ).apply() @@ -567,18 +577,18 @@ async def end_game_stamps(ninja, finish_position): if finish_position == 1: await ninja.penguin.update(fire_matches_won=ninja.penguin.fire_matches_won + 1).apply() if ninja.penguin.fire_matches_won >= 10: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[252]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.WARM_UP) if ninja.penguin.fire_matches_won >= 50: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[268]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.FIRE_EXPERT) if ninja.energy >= 6: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[260]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.STRONG_DEFENCE) if type(ninja.penguin.waddle) == FireSenseiLogic: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[264]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.FIRE_NINJA) if ninja.energy_won >= 1: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[254]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.SCORE_FIRE) if ninja.energy_won >= 3: - await ninja.penguin.add_stamp(ninja.penguin.server.stamps[266]) + await ninja.penguin.add_card_jitsu_stamp(FireStamp.MAX_ENERGY) @handlers.handler(XTPacket('gz', ext='z')) @@ -665,5 +675,5 @@ async def handle_info_ready_sync(p): @handlers.handler(XTPacket('lz', ext='z')) @handlers.player_in_room(CardJitsuFireLogic.room_id) -async def handle_leave_game(p): - await ninja_stamps_earned(p) +async def handle_leave_game(p: Penguin): + await p.send_card_jitsu_stamp_info() diff --git a/houdini/handlers/games/ninja/water.py b/houdini/handlers/games/ninja/water.py index 0a91044..99618a5 100644 --- a/houdini/handlers/games/ninja/water.py +++ b/houdini/handlers/games/ninja/water.py @@ -12,6 +12,17 @@ from houdini.handlers import XTPacket from houdini.penguin import Penguin from houdini.data.ninja import Card +class WaterStamp(enum.IntEnum): + "IDs of Card-Jitsu Water stamps" + GONG = 270 + WATERY_FALL = 274 + WATER_EXPERT = 276 + WATER_MIDWAY = 278 + WATER_SUIT = 282 + WATER_NINJA = 284 + TWO_CLOSE = 286 + SKIPPING_STONES = 288 + @dataclass class WaterCard: @@ -252,6 +263,13 @@ class WaterPlayer: cleared: int = 0 """Number of stones cleared in the match""" + left: bool = False + """ + Whether the player has left the game + + Penguins remain in the board even if the player leaves + """ + def get_card(self, hand_id: int) -> WaterCard: """Get the card given its hand ID""" return next((card for card in self.hand.cards if card.hand_id == hand_id), None) @@ -407,7 +425,11 @@ class CardJitsuWaterLogic(IWaddle): ITEM_AWARDS = [6026, 4121, 2025, 1087, 3032] """All the items gained from ranking, indexed by their rank""" - STAMP_AWARDS = {1: 278, 3: 282, 4: 284} + STAMP_AWARDS = { + 1: WaterStamp.WATER_MIDWAY, + 3: WaterStamp.WATER_SUIT, + 4: WaterStamp.WATER_NINJA, + } """Map rank and the stamp you gain from LEAVING the rank""" board_cycle_handler: WaterCycleHandler @@ -469,6 +491,13 @@ class CardJitsuWaterLogic(IWaddle): # original) self.board = Board(columns=5 if len(waddle.penguins) <= 2 else 7) + async def remove_penguin(self, p: Penguin): + player = self.get_player_by_penguin(p) + # this may run twice for the same player + if player is not None: + self.players[player.seat_id].left = True + await super().remove_penguin(p) + async def send_zm(self, *args): """Send a "zm" packet, used for various commands, to the clients""" await self.send_xt("zm", "&".join(map(str, args))) @@ -477,9 +506,11 @@ class CardJitsuWaterLogic(IWaddle): """Send a "zm" packet, used for various commands, to a specific client""" await player.penguin.send_xt("zm", "&".join(map(str, args))) - def get_player_by_penguin(self, penguin: Penguin) -> WaterPlayer: + def get_player_by_penguin(self, penguin: Penguin) -> Union[WaterPlayer, None]: """Get the player instance associated with a penguin""" - return next(player for player in self.players if player.penguin == penguin) + return next( + (player for player in self.players if player.penguin == penguin), None + ) def get_card_generator(self, p: Penguin) -> Generator[WaterCard, None, None]: """Get a generator for a player's cards""" @@ -634,15 +665,12 @@ class CardJitsuWaterLogic(IWaddle): ).apply() if winner.penguin.water_matches_won >= 100: - # Water Expert stamp - await winner.penguin.add_stamp(winner.penguin.server.stamps[276]) + await winner.penguin.add_card_jitsu_stamp(WaterStamp.WATER_EXPERT) - # Gong! stamp - await winner.penguin.add_stamp(winner.penguin.server.stamps[270]) + await winner.penguin.add_card_jitsu_stamp(WaterStamp.GONG) if winner.two_close >= 2: - # Two Close stamp - await winner.penguin.add_stamp(winner.penguin.server.stamps[286]) + await winner.penguin.add_card_jitsu_stamp(WaterStamp.TWO_CLOSE) # iterate over all players that drowned from the last place order for row in self.board.rows: @@ -650,7 +678,7 @@ class CardJitsuWaterLogic(IWaddle): break players_in_row = self.get_players_in_row(row) for player in players_in_row: - if isinstance(player, WaterSensei): + if isinstance(player, WaterSensei) or player.left: continue # because winner has already been removed @@ -684,7 +712,7 @@ class CardJitsuWaterLogic(IWaddle): p.server.items[cls.ITEM_AWARDS[rank]], cost=0, notify=False ) if rank in cls.STAMP_AWARDS: - await p.add_stamp(p.server.stamps[cls.STAMP_AWARDS[rank]]) + await p.add_card_jitsu_stamp(cls.STAMP_AWARDS[rank]) await p.update(water_ninja_rank=p.water_ninja_rank + ranks).apply() return True @@ -700,20 +728,28 @@ class CardJitsuWaterLogic(IWaddle): players_in_row = self.get_players_in_row(drop_row) position = len(self.players) for player in players_in_row: - if player.penguin is not None: + if not player.left: # Watery Fall stamp await player.penguin.add_stamp(player.penguin.server.stamps[274]) + + await player.penguin.add_card_jitsu_stamp(WaterStamp.WATERY_FALL) + # CMD_PLAYER_KILL, meant for players who lose from falling player_kill_data = [] for player in players_in_row: - amulet = await self.update_player_progress( - player, fell=True, position=position - ) + amulet = None + if not player.left: + amulet = await self.update_player_progress( + player, fell=True, position=position + ) + else: + amulet = Amulet(None, False) player_kill_data.append( f"pk&{player.seat_id}&{position}&{amulet.serialize()}&false" ) + await self.send_zm(":".join(player_kill_data)) # for Two Close stamp @@ -985,12 +1021,15 @@ def get_water_rank_threshold(rank): async def handle_get_game(p: Penguin): """Handle the client entering the game""" seat_id = p.waddle.get_seat_id(p) - player = p.waddle.get_player_by_penguin(p) + player: WaterPlayer = p.waddle.get_player_by_penguin(p) # needs to send these or the client dies await p.send_xt("gz") await p.send_xt("jz") + # needed to fix client taking a bit to exit game + await p.send_card_jitsu_stamp_info() + # CMD_PLAYER_INDEX await p.waddle.send_zm_client(player, "po", seat_id) @@ -1109,8 +1148,7 @@ async def handle_throw_card(p: Penguin, *, cell_id: str): ) if player.cleared >= 28: - # Skipping Stones stamp - await player.penguin.add_stamp(player.penguin.server.stamps[288]) + await p.add_card_jitsu_stamp(WaterStamp.SKIPPING_STONES) # CMD_PLAYER_THROW await p.waddle.send_zm( diff --git a/houdini/penguin.py b/houdini/penguin.py index f3ccb67..15d0033 100644 --- a/houdini/penguin.py +++ b/houdini/penguin.py @@ -244,6 +244,8 @@ class Penguin(Spheniscidae, penguin.Penguin): if stamp.id in self.stamps: return False + await self.server.redis.set(self.get_recent_stamp_key(stamp.id), 1) + await self.stamps.insert(stamp_id=stamp.id) if notify: @@ -254,6 +256,10 @@ class Penguin(Spheniscidae, penguin.Penguin): return True + def get_recent_stamp_key(self, stamp_id): + """Get redis key that locates the session recency of a stamp""" + return f'{self.id}.{stamp_id}.recentstamp' + async def add_inbox(self, postcard, sender_name="sys", sender_id=None, details=""): penguin_postcard = await PenguinPostcard.create(penguin_id=self.id, sender_id=sender_id, postcard_id=postcard.id, details=details) @@ -380,7 +386,90 @@ class Penguin(Spheniscidae, penguin.Penguin): self.logger.info(f'{self.username} updated their background to \'{item.name}\' ' if item else f'{self.username} removed their background item') + + async def send_card_jitsu_stamp_info(self): + """ + Send information the client requires to properly display the stamp end screen + """ + stamp_info = await self.get_game_end_stamps_info(False) + await self.send_xt("cjsi", *stamp_info) + + async def add_card_jitsu_stamp(self, stamp_id): + """Correct way of adding a card-jitsu (Regular, Fire, Water) stamp""" + await self.add_stamp(self.server.stamps[stamp_id]) + await self.send_card_jitsu_stamp_info() + + async def get_game_end_stamps_info( + self, clear_session: bool + ) -> tuple[str, int, int, int]: + """ + Get the info of the stamps at the end of a game that the client requires + If clear_session is True, the stamps will be marked off and will no longer + show up as new stamps at the end of minigames + """ + ( + collected_game_stamps_string, + total_collected_game_stamps, + total_game_stamps, + total_stamps, + ) = ("", 0, 0, 0) + + game_stamps = [ + stamp + for stamp in self.server.stamps.values() + if stamp.group_id == self.room.stamp_group + ] + + game_stamps_ids = [stamp.id for stamp in game_stamps] + + recently_collected_game_stamps = [] + + for stamp in self.stamps.values(): + if stamp.stamp_id in game_stamps_ids: + is_recent = await self.server.redis.get( + self.get_recent_stamp_key(stamp.stamp_id) + ) + if is_recent: + recently_collected_game_stamps.append(stamp) + + collected_game_stamps = [ + stamp for stamp in game_stamps if (stamp.id in self.stamps and stamp) + ] + + collected_game_stamps_string = "|".join( + str(stamp.stamp_id) for stamp in recently_collected_game_stamps + ) + + total_collected_game_stamps = len(collected_game_stamps) + + total_game_stamps = len(game_stamps) + + total_stamps = len( + [ + stamp + for stamp in self.stamps.values() + if self.server.stamps[stamp.stamp_id].group_id + ] + ) + + if clear_session: + await self.clear_stamps_session() + + return ( + collected_game_stamps_string, + total_collected_game_stamps, + total_game_stamps, + total_stamps, + ) + + async def clear_stamps_session(self): + """ + Exits a game session and unmarks all stamps since we are no longer in their session + """ + async for key in self.server.redis.scan_iter(self.get_recent_stamp_key("*")): + await self.server.redis.delete(key) + def __repr__(self): if self.id is not None: return f''