From 2b4643b2374a5e75b91ec1a58d9dde03c8122b0b Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Fri, 20 Jan 2023 20:57:30 +0000 Subject: [PATCH 01/27] add game save --- backend/bang/cards.py | 17 +-- backend/bang/characters.py | 3 +- backend/bang/expansions/dodge_city/cards.py | 7 +- .../bang/expansions/gold_rush/shop_cards.py | 15 ++- .../expansions/the_valley_of_shadows/cards.py | 5 +- backend/bang/game.py | 67 +++++----- backend/bang/players.py | 109 ++++++++-------- backend/globals.py | 6 + backend/server.py | 35 +++-- backend/tests/__init__.py | 3 + backend/tests/cards_test.py | 121 +++++++++--------- backend/tests/character_test.py | 97 +++++++------- backend/tests/deck_test.py | 8 +- backend/tests/game_test.py | 43 +++---- backend/tests/roles_test.py | 36 ++---- backend/tests/valley_of_shadows_test.py | 51 +++----- 16 files changed, 317 insertions(+), 306 deletions(-) create mode 100644 backend/globals.py diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 0d340e0..cac31ed 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -4,6 +4,7 @@ import bang.expansions.high_noon.card_events as ceh from abc import ABC, abstractmethod from enum import IntEnum import bang.roles as r +from globals import G class Suit(IntEnum): DIAMONDS = 0 # ♦ @@ -84,10 +85,10 @@ class Card(ABC): player.equipment.append(self) self.can_be_used_now = False if against: - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') else: - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') return True @@ -154,7 +155,7 @@ class Prigione(Card): return False if against != None and not isinstance(player.game.get_player_named(against).role, r.Sheriff): self.can_be_used_now = False - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') player.game.get_player_named(against).equipment.append(self) player.game.get_player_named(against).notify_self() @@ -268,7 +269,7 @@ class Birra(Card): player.lives = min(player.lives+1, player.max_lives) return True elif len(player.game.get_alive_players()) == 2 or player.lives == player.max_lives: - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_spilled_beer|{player.name}|{self.name}') return True return False @@ -305,7 +306,7 @@ class Diligenza(Card): # self.desc_eng = "Draw 2 cards from the deck." def play_card(self, player, against, _with=None): - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_diligenza|{player.name}|{self.name}') for i in range(2): player.hand.append(player.game.deck.draw(True)) @@ -382,7 +383,7 @@ class Mancato(Card): return False if player.game.check_event(ceh.Sermone): return False - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_special_calamity|{player.name}|{self.name}|{against}') player.bang_used += 1 player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1 @@ -421,7 +422,7 @@ class Saloon(Card): self.alt_text = "👥🍺" def play_card(self, player, against, _with=None): - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_saloon|{player.name}|{self.name}') for p in player.game.get_alive_players(): p.lives = min(p.lives+1, p.max_lives) @@ -438,7 +439,7 @@ class WellsFargo(Card): self.alt_text = "🎴🎴🎴" def play_card(self, player, against, _with=None): - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_wellsfargo|{player.name}|{self.name}') for i in range(3): player.hand.append(player.game.deck.draw(True)) diff --git a/backend/bang/characters.py b/backend/bang/characters.py index 460f996..2925e21 100644 --- a/backend/bang/characters.py +++ b/backend/bang/characters.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod from bang.expansions import * from typing import List +from globals import G class Character(ABC): def __init__(self, name: str, max_lives: int, sight_mod: int = 0, visibility_mod: int = 0, pick_mod: int = 0, desc: str = ''): @@ -25,7 +26,7 @@ class Character(ABC): import bang.expansions.high_noon.card_events as ceh if player.game.check_event(ceh.Sbornia): return False - player.sio.emit('chat_message', room=player.game.name, data=f'_use_special|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_use_special|{player.name}|{self.name}') return True class BartCassidy(Character): diff --git a/backend/bang/expansions/dodge_city/cards.py b/backend/bang/expansions/dodge_city/cards.py index af0f7aa..2cd0ee2 100644 --- a/backend/bang/expansions/dodge_city/cards.py +++ b/backend/bang/expansions/dodge_city/cards.py @@ -1,5 +1,6 @@ from bang.cards import * import bang.expansions.fistful_of_cards.card_events as ce +from globals import G class Binocolo(Mirino): def __init__(self, suit, number): @@ -88,7 +89,7 @@ class Rissa(CatBalou): player.event_type = 'rissa' print(f'rissa targets: {player.rissa_targets}') super().play_card(player, against=player.rissa_targets.pop(0).name) - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') return True return False @@ -123,7 +124,7 @@ class Tequila(Card): def play_card(self, player, against, _with=None): if against != None and _with != None: - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card_for|{player.name}|{self.name}|{against}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_for|{player.name}|{self.name}|{against}') player.game.deck.scrap(_with) player.game.get_player_named(against).lives = min(player.game.get_player_named(against).lives+1, player.game.get_player_named(against).max_lives) player.game.get_player_named(against).notify_self() @@ -322,7 +323,7 @@ class CanCan(CatBalou): def play_card(self, player, against, _with=None): if self.can_be_used_now: - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') return super().play_card(player, against) else: if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice): diff --git a/backend/bang/expansions/gold_rush/shop_cards.py b/backend/bang/expansions/gold_rush/shop_cards.py index 9424177..49ae8ed 100644 --- a/backend/bang/expansions/gold_rush/shop_cards.py +++ b/backend/bang/expansions/gold_rush/shop_cards.py @@ -1,6 +1,7 @@ from bang.cards import * import bang.roles as r import bang.players as pl +from globals import G class ShopCardKind(IntEnum): BROWN = 0 # Se l’equipaggiamento ha il bordo marrone, applicane subito l’effetto e poi scartalo. @@ -16,14 +17,14 @@ class ShopCard(Card): def play_card(self, player, against, _with=None): if self.kind == ShopCardKind.BROWN: - player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') return True elif self.kind == ShopCardKind.BLACK: # equip it if not self.is_duplicate_card(player): self.reset_card() self.can_be_used_now = True player.gold_rush_equipment.append(self) - player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') return True else: return False @@ -102,7 +103,7 @@ class Rum(ShopCard): num = 5 if player.character.check(player.game, c.LuckyDuke) else 4 for i in range(num): c = player.game.deck.pick_and_scrap() - player.sio.emit('chat_message', room=player.game.name, data=f'_flipped|{player.name}|{c.name}|{c.num_suit()}') + G.sio.emit('chat_message', room=player.game.name, data=f'_flipped|{player.name}|{c.name}|{c.num_suit()}') suits.add(c.suit) player.lives = min(player.lives+len(suits), player.max_lives) return super().play_card(player, against, _with) @@ -113,7 +114,7 @@ class UnionPacific(ShopCard): self.icon = '🚆️' def play_card(self, player, against=None, _with=None): - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_UnionPacific|{player.name}|{self.name}') for i in range(4): player.hand.append(player.game.deck.draw(True)) @@ -162,7 +163,7 @@ class Ricercato(ShopCard): self.can_target_self = True def play_card(self, player, against=None, _with=None): - player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}') player.available_cards = [{ 'name': p.name, 'icon': p.role.icon if(player.game.initial_players == 3) else '🤠', @@ -187,7 +188,7 @@ class Setaccio(ShopCard): return super().play_card(player, against, _with) else: if player.gold_nuggets >= 1 and player.setaccio_count < 2: - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') player.gold_nuggets -= 1 player.setaccio_count += 1 player.hand.append(player.game.deck.draw(True)) @@ -224,7 +225,7 @@ class Zaino(ShopCard): return super().play_card(player, against, _with) else: if player.gold_nuggets >= 2: - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}') player.gold_nuggets -= 2 player.lives = min(player.lives + 1, player.max_lives) player.notify_self() diff --git a/backend/bang/expansions/the_valley_of_shadows/cards.py b/backend/bang/expansions/the_valley_of_shadows/cards.py index d5ec17e..d366a41 100644 --- a/backend/bang/expansions/the_valley_of_shadows/cards.py +++ b/backend/bang/expansions/the_valley_of_shadows/cards.py @@ -3,6 +3,7 @@ import bang.roles as r import bang.players as pl from bang.cards import Card, Suit, Bang, Mancato import bang.expansions.fistful_of_cards.card_events as ce +from globals import G class Fantasma(Card): def __init__(self, suit, number): @@ -46,7 +47,7 @@ class SerpenteASonagli(Card): return False if against != None: self.can_be_used_now = False - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') player.game.get_player_named(against).equipment.append(self) player.game.get_player_named(against).notify_self() @@ -69,7 +70,7 @@ class Taglia(Card): return False if against != None: self.can_be_used_now = False - player.sio.emit('chat_message', room=player.game.name, + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|{self.name}|{against}') player.game.get_player_named(against).equipment.append(self) player.game.get_player_named(against).notify_self() diff --git a/backend/bang/game.py b/backend/bang/game.py index 4539e42..ffbcb4f 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -15,6 +15,8 @@ import bang.expansions.high_noon.card_events as ceh import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.characters as grch from metrics import Metrics +from globals import G + debug_commands = [ {'cmd':'/debug', 'help':'Toggles the debug mode'}, @@ -45,9 +47,8 @@ debug_commands = [ ] class Game: - def __init__(self, name, sio:socketio): + def __init__(self, name): super().__init__() - self.sio = sio self.name = name self.players: List[pl.Player] = [] self.spectators: List[pl.Player] = [] @@ -134,7 +135,7 @@ class Game: self.expansions = json.loads(cmd[4].replace("'",'"')) pnames = json.loads(cmd[3].replace("'",'"')) for p in pnames: - self.add_player(pl.Player(p, p, DummySocket(self.sio), bot=False)) + self.add_player(pl.Player(p, p, DummySocket(G.sio), bot=False)) continue if cmd[1] == 'start_game': self.start_game(int(cmd[2])) @@ -181,7 +182,7 @@ class Game: def notify_room(self, sid=None): if any((p.character == None for p in self.players)) or sid: - self.sio.emit('room', room=self.name if not sid else sid, data={ + G.sio.emit('room', room=self.name if not sid else sid, data={ 'name': self.name, 'started': self.started, 'players': [{'name':p.name, 'ready': p.character != None, 'is_bot': p.is_bot, 'avatar': p.avatar} for p in self.players], @@ -192,12 +193,12 @@ class Game: 'available_expansions': self.available_expansions, 'is_replay': self.is_replay, }) - self.sio.emit('debug', room=self.name, data=self.debug) + G.sio.emit('debug', room=self.name, data=self.debug) if self.debug: - self.sio.emit('commands', room=self.name, data=[x for x in debug_commands if 'admin' not in x]) + G.sio.emit('commands', room=self.name, data=[x for x in debug_commands if 'admin' not in x]) else: - self.sio.emit('commands', room=self.name, data=[{'cmd':'/debug', 'help':'Toggles the debug mode'}]) - self.sio.emit('spectators', room=self.name, data=len(self.spectators)) + G.sio.emit('commands', room=self.name, data=[{'cmd':'/debug', 'help':'Toggles the debug mode'}]) + G.sio.emit('spectators', room=self.name, data=len(self.spectators)) def toggle_expansion(self, expansion_name): if not self.started: @@ -235,7 +236,7 @@ class Game: self.players.append(player) print(f'{self.name}: Added player {player.name} to game') self.notify_room() - self.sio.emit('chat_message', room=self.name, data=f'_joined|{player.name}') + G.sio.emit('chat_message', room=self.name, data=f'_joined|{player.name}') def set_private(self): if not self.is_changing_pwd: @@ -254,7 +255,7 @@ class Game: if not any((p.character == None for p in self.players)): for i in range(len(self.players)): print(self.name, self.players[i].name, self.players[i].character) - self.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}') + G.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}') self.players[i].prepare() for k in range(self.players[i].max_lives): self.players[i].hand.append(self.deck.draw()) @@ -265,7 +266,7 @@ class Game: for x in current_roles: if (x not in cr): cr += '|' +x + '|' + str(current_roles.count(x)) - self.sio.emit('chat_message', room=self.name, data=f'_allroles{cr}') + G.sio.emit('chat_message', room=self.name, data=f'_allroles{cr}') self.play_turn() def choose_characters(self): @@ -286,8 +287,8 @@ class Game: self.rpc_log = [f';players;{len(self.players)};{[p.name for p in self.players]};{self.expansions}', f';start_game;{SEED}'] self.rng = random.Random(SEED) self.players_map = {c.name: i for i, c in enumerate(self.players)} - self.sio.emit('chat_message', room=self.name, data=f'_starting') - self.sio.emit('start', room=self.name) + G.sio.emit('chat_message', room=self.name, data=f'_starting') + G.sio.emit('start', room=self.name) self.started = True self.someone_won = False self.attack_in_progress = False @@ -319,7 +320,7 @@ class Game: self.players[i].set_role(available_roles[i]) if isinstance(available_roles[i], roles.Sheriff) or (len(available_roles) == 3 and isinstance(available_roles[i], roles.Vice)): if isinstance(available_roles[i], roles.Sheriff): - self.sio.emit('chat_message', room=self.name, data=f'_sheriff|{self.players[i].name}') + G.sio.emit('chat_message', room=self.name, data=f'_sheriff|{self.players[i].name}') self.turn = i self.players[i].notify_self() self.notify_event_card() @@ -420,13 +421,13 @@ class Game: self.players[self.turn].pending_action = pl.PendingAction.CHOOSE self.players[self.turn].choose_text = 'choose_card_to_get' self.players[self.turn].available_cards = self.available_cards - self.sio.emit('emporio', room=self.name, data=json.dumps( + G.sio.emit('emporio', room=self.name, data=json.dumps( {'name':self.players[self.turn].name,'cards': self.available_cards}, default=lambda o: o.__dict__)) self.players[self.turn].notify_self() def respond_emporio(self, player, i): card = self.available_cards.pop(i) - player.sio.emit('chat_message', room=self.name, data=f'_choose_emporio|{player.name}|{card.name}') + G.sio.emit('chat_message', room=self.name, data=f'_choose_emporio|{player.name}|{card.name}') player.hand.append(card) player.available_cards = [] player.pending_action = pl.PendingAction.WAIT @@ -436,18 +437,18 @@ class Game: if len(self.available_cards) == 1: nextPlayer.hand.append(self.available_cards.pop()) nextPlayer.notify_self() - self.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}') + G.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}') self.players[self.turn].pending_action = pl.PendingAction.PLAY self.players[self.turn].notify_self() elif nextPlayer == self.players[self.turn]: - self.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}') + G.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}') self.players[self.turn].pending_action = pl.PendingAction.PLAY self.players[self.turn].notify_self() else: nextPlayer.pending_action = pl.PendingAction.CHOOSE nextPlayer.choose_text = 'choose_card_to_get' nextPlayer.available_cards = self.available_cards - self.sio.emit('emporio', room=self.name, data=json.dumps( + G.sio.emit('emporio', room=self.name, data=json.dumps( {'name':nextPlayer.name,'cards': self.available_cards}, default=lambda o: o.__dict__)) nextPlayer.notify_self() @@ -533,15 +534,15 @@ class Game: if p.win_status and not (isinstance(p.role, roles.Renegade) and p.is_dead): if not self.someone_won: self.someone_won = True - self.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}|{p.role.name}') + G.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}|{p.role.name}') if not self.is_replay: Metrics.send_metric('player_win', points=[1], tags=[f"char:{p.character.name}", f"role:{p.role.name}"]) p.notify_self() - if hasattr(self.sio, 'is_fake'): + if hasattr(G.sio, 'is_fake'): print('announces_winners(): Running for tests, you will have to call reset manually!') return for i in range(5): - self.sio.emit('chat_message', room=self.name, data=f'_lobby_reset|{5-i}') + G.sio.emit('chat_message', room=self.name, data=f'_lobby_reset|{5-i}') eventlet.sleep(1) return self.reset() @@ -575,7 +576,7 @@ class Game: self.deck.flip_event() if len(self.deck.event_cards) > 0 and self.deck.event_cards[0] != None: print(f'{self.name}: flip new event {self.deck.event_cards[0].name}') - self.sio.emit('chat_message', room=self.name, data={'color': f'orange','text':f'_flip_event|{self.deck.event_cards[0].name}'}) + G.sio.emit('chat_message', room=self.name, data={'color': f'orange','text':f'_flip_event|{self.deck.event_cards[0].name}'}) if self.check_event(ce.DeadMan): self.did_resuscitate_deadman = False elif self.check_event(ce.RouletteRussa): @@ -592,7 +593,7 @@ class Game: for p in hurt_players: if p.lives != p.max_lives: p.lives += 1 - self.sio.emit('chat_message', room=self.name, data=f'_doctor_heal|{p.name}') + G.sio.emit('chat_message', room=self.name, data=f'_doctor_heal|{p.name}') p.notify_self() elif self.check_event(ceh.IDalton): self.waiting_for = 0 @@ -633,29 +634,29 @@ class Game: if len(self.deck.event_cards) > 0: room = self.name if sid == None else sid if self.deck.event_cards[0] != None: - self.sio.emit('event_card', room=room, data=self.deck.event_cards[0].__dict__) + G.sio.emit('event_card', room=room, data=self.deck.event_cards[0].__dict__) else: - self.sio.emit('event_card', room=room, data=None) + G.sio.emit('event_card', room=room, data=None) def notify_gold_rush_shop(self, sid=None): if 'gold_rush' in self.expansions and self.deck and self.deck.shop_cards and len(self.deck.shop_cards) > 0: room = self.name if sid == None else sid print(f'{self.name}: gold_rush_shop room={room}, data={self.deck.shop_cards}') - self.sio.emit('gold_rush_shop', room=room, data=json.dumps(self.deck.shop_cards, default=lambda o: o.__dict__)) + G.sio.emit('gold_rush_shop', room=room, data=json.dumps(self.deck.shop_cards, default=lambda o: o.__dict__)) def notify_scrap_pile(self, sid=None): print(f'{self.name}: scrap') room = self.name if sid == None else sid if self.deck.peek_scrap_pile(): - self.sio.emit('scrap', room=room, data=self.deck.peek_scrap_pile().__dict__) + G.sio.emit('scrap', room=room, data=self.deck.peek_scrap_pile().__dict__) else: - self.sio.emit('scrap', room=room, data=None) + G.sio.emit('scrap', room=room, data=None) def handle_disconnect(self, player: pl.Player): print(f'{self.name}: player {player.name} left the game') if player in self.spectators: self.spectators.remove(player) - self.sio.emit('spectators', room=self.name, data=len(self.spectators)) + G.sio.emit('spectators', room=self.name, data=len(self.spectators)) return False if player.is_bot and not self.started: player.game = None @@ -735,9 +736,9 @@ class Game: # if not disconnected: # self.dead_players.append(corpse) self.notify_room() - self.sio.emit('chat_message', room=self.name, data=f'_died|{player.name}') + G.sio.emit('chat_message', room=self.name, data=f'_died|{player.name}') if self.started: - self.sio.emit('chat_message', room=self.name, data=f'_died_role|{player.name}|{player.role.name}') + G.sio.emit('chat_message', room=self.name, data=f'_died_role|{player.name}|{player.role.name}') for p in self.players: if not p.is_bot: p.notify_self() @@ -854,4 +855,4 @@ class Game: 'is_ghost': p.is_ghost, 'is_bot': p.is_bot, } for p in self.get_alive_players()] - self.sio.emit('players_update', room=self.name, data=data) + G.sio.emit('players_update', room=self.name, data=data) diff --git a/backend/bang/players.py b/backend/bang/players.py index da300d1..0b077e3 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -16,6 +16,7 @@ import bang.expansions.the_valley_of_shadows.cards as tvosc import eventlet from typing import List from metrics import Metrics +from globals import G robot_pictures = [ 'https://i.imgur.com/40rAFIb.jpg', @@ -63,25 +64,24 @@ class Player: else: self.avatar = f'https://cdn.discordapp.com/avatars/{res["id"]}/{res["avatar"]}.png' if self.game: - self.sio.emit('chat_message', room=self.game.name, data=f'_change_username|{self.name}|{res["username"]}') + G.sio.emit('chat_message', room=self.game.name, data=f'_change_username|{self.name}|{res["username"]}') self.name = res['username'] self.discord_id = res['id'] if self.is_admin(): if self.game: self.game.feature_flags() - self.sio.emit('chat_message', room=self.sid, data={'color':'green', 'text':'(you are admin)'}) + G.sio.emit('chat_message', room=self.sid, data={'color':'green', 'text':'(you are admin)'}) if self.game: self.game.notify_room() - self.sio.emit('me', data=self.name, room=self.sid) + G.sio.emit('me', data=self.name, room=self.sid) else: print('error getting avatar', r.status_code, r.text) print(r) - def __init__(self, name, sid, sio, bot=False, discord_token=None): + def __init__(self, name, sid, bot=False, discord_token=None): import bang.game as g super().__init__() self.name = name self.sid = sid - self.sio = sio self.is_bot = bot self.discord_token = discord_token self.discord_id = None @@ -89,7 +89,7 @@ class Player: if self.is_bot: self.avatar = robot_pictures[randrange(len(robot_pictures))] if self.discord_token: - sio.start_background_task(self.get_avatar) + G.sio.start_background_task(self.get_avatar) self.game: g = None self.reset() @@ -155,7 +155,7 @@ class Player: def set_role(self, role: r.Role): self.role = role print(f'{self.name}: I am a {role.name}, my goal is "{role.goal}"') - self.sio.emit('role', room=self.sid, data=json.dumps( + G.sio.emit('role', room=self.sid, data=json.dumps( role, default=lambda o: o.__dict__)) def set_character(self, character: str): @@ -174,14 +174,14 @@ class Player: self.real_character = self.character self.available_characters = [] print(f'{self.name}: I chose character {self.character.name}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_did_choose_character|{self.name}') self.game.notify_character_selection() elif self.real_character and isinstance(self.real_character, chd.VeraCuster): self.character = next( x for x in self.available_characters if x.name == character) self.available_characters = [] - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_did_choose_character|{self.name}') self.pending_action = PendingAction.DRAW self.notify_self() @@ -199,7 +199,7 @@ class Player: self.available_characters = available print(f'{self.name}: I have to choose between {available}') if not self.is_bot: - self.sio.emit('characters', room=self.sid, data=json.dumps( + G.sio.emit('characters', room=self.sid, data=json.dumps( available, default=lambda o: o.__dict__)) else: char_name = available[randrange(0, len(available))].name @@ -217,7 +217,7 @@ class Player: 'message':message } print('notifying card') - self.sio.emit('notify_card', room=self.sid, data=mess) + G.sio.emit('notify_card', room=self.sid, data=mess) def notify_self(self): if any((True for c in self.equipment if isinstance(c, tvosc.Fantasma))): @@ -272,7 +272,6 @@ class Player: ser = self.__dict__.copy() ser.pop('game') - ser.pop('sio') ser.pop('sid') ser.pop('on_pick_cb') ser.pop('discord_token') @@ -292,16 +291,16 @@ class Player: self.pending_action = PendingAction.WAIT ser['hand'] = [] ser['equipment'] = [] - self.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__)) + G.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__)) self.game.player_death(self) if self.game and self.game.started: # falso quando un bot viene eliminato dalla partita - self.sio.emit('self_vis', room=self.sid, data=json.dumps(self.game.get_visible_players(self), default=lambda o: o.__dict__)) + G.sio.emit('self_vis', room=self.sid, data=json.dumps(self.game.get_visible_players(self), default=lambda o: o.__dict__)) self.game.notify_all() - self.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__)) + G.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__)) def bot_spin(self): while self.is_bot and self.game != None and not self.game.shutting_down: - eventlet.sleep(max(0.2, uniform(self.game.bot_speed/2-0.1, self.game.bot_speed))) + G.sio.sleep(max(0.2, uniform(self.game.bot_speed/2-0.1, self.game.bot_speed))) if self.lives > 0 or self.is_ghost: self.bot_logic() @@ -441,7 +440,7 @@ class Player: self.is_playing_ranch = False self.can_play_vendetta = can_play_vendetta if not again: - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_turn|{self.name}') print(f'{self.name}: I was notified that it is my turn') self.was_shot = False @@ -461,7 +460,7 @@ class Player: self.hand.append(self.game.deck.draw(True)) if self.character.check(self.game, chars.BartCassidy) and self.lives > 0: self.hand.append(self.game.deck.draw(True)) - self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') self.heal_if_needed() if self.lives <= 0: return self.notify_self() @@ -557,11 +556,11 @@ class Player: for i in range(num): if i == 0 and pile == 'scrap' and self.character.check(self.game, chars.PedroRamirez): self.hand.append(self.game.deck.draw_from_scrap_pile()) - self.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_scrap|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_scrap|{self.name}') elif i == 0 and type(pile) == str and pile != self.name and pile in self.game.players_map and self.character.check(self.game, chars.JesseJones) and len(self.game.get_player_named(pile).hand) > 0: self.hand.append(self.game.get_player_named(pile).hand.pop( randrange(0, len(self.game.get_player_named(pile).hand)))) self.game.get_player_named(pile).notify_self() - self.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_player|{self.name}|{pile}') + G.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_player|{self.name}|{pile}') elif i == 1: card: cs.Card = self.game.deck.draw() if (self.character.check(self.game, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest)): @@ -602,7 +601,7 @@ class Player: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f'Did pick {picked}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0: self.lives -= 3 @@ -616,12 +615,12 @@ class Player: self.hand.append(self.game.deck.draw()) self.attacker = None self.game.deck.scrap(self.equipment.pop(i), True) - self.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}') self.heal_if_needed() if self.character.check(self.game, chars.BartCassidy) and self.lives > 0: for i in range(3): self.hand.append(self.game.deck.draw(True)) - self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') print(f'{self.name} Boom, -3 hp') break else: @@ -637,16 +636,16 @@ class Player: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f'Did pick {picked}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if not picked.check_suit(self.game, [cs.Suit.HEARTS]) and pickable_cards == 0: self.game.deck.scrap(self.equipment.pop(i), True) - self.sio.emit('chat_message', room=self.game.name, data=f'_prison_turn|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_prison_turn|{self.name}') self.end_turn(forced=True) return elif pickable_cards == 0: self.game.deck.scrap(self.equipment.pop(i), True) - self.sio.emit('chat_message', room=self.game.name, data=f'_prison_free|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_prison_free|{self.name}') break break for i in range(len(self.equipment)): @@ -655,13 +654,13 @@ class Player: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f'Did pick {picked}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if not picked.check_suit(self.game, [cs.Suit.SPADES]): break elif pickable_cards == 0: self.lives -= 1 - self.sio.emit('chat_message', room=self.game.name, data=f'_snake_bit|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_snake_bit|{self.name}') break if any((isinstance(c, cs.Prigione) for c in self.equipment)): self.notify_self() #TODO perchè solo le prigioni? e multiple dinamiti come si comportano con veracuster? @@ -725,7 +724,7 @@ class Player: self.hand.insert(hand_index, card) if withCard: self.hand.insert(_with, withCard) - self.sio.emit('cant_play_card', room=self.sid) + G.sio.emit('cant_play_card', room=self.sid) elif (card.usable_next_turn and card.can_be_used_now) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK): if did_play_card: self.game.deck.scrap(card, True) @@ -773,7 +772,7 @@ class Player: self.notify_self() elif self.choose_text == 'choose_ricercato': player = self.game.get_player_named(self.available_cards[card_index]['name']) - player.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Ricercato|{player.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Ricercato|{player.name}') if any((isinstance(c, grc.Ricercato) for c in player.gold_rush_equipment)): self.game.deck.shop_deck.append(grc.Ricercato()) else: @@ -793,20 +792,20 @@ class Player: self.notify_self() elif self.choose_text == 'choose_bicchierino': player = self.game.get_player_named(self.available_cards[card_index]['name']) - self.sio.emit('chat_message', room=self.game.name, data=f'_play_card_for|{self.name}|{"Bicchierino"}|{player.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_play_card_for|{self.name}|{"Bicchierino"}|{player.name}') player.lives = min(player.lives+1, player.max_lives) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == 'choose_birra_function': if card_index == 0: self.gold_nuggets += 1 - self.sio.emit('chat_message', room=self.game.name, data=f'_get_nugget|{self.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_get_nugget|{self.name}') else: cs.Birra(1,1).play_card(self, skipChecks=True) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == 'choose_bottiglia': - self.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}') + G.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}') if isinstance(self.available_cards[card_index], cs.Birra): self.lives = min(self.lives+1, self.max_lives) else: @@ -814,7 +813,7 @@ class Player: self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == 'choose_complice': - self.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}') + G.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}') self.hand.append(self.available_cards[card_index]) self.pending_action = PendingAction.PLAY self.notify_self() @@ -835,7 +834,7 @@ class Player: player = self.game.get_player_named(self.choose_text.split('|')[1]) player.gold_rush_equipment.remove(self.available_cards[card_index]) self.game.deck.shop_deck.append(self.available_cards[card_index]) - self.sio.emit('chat_message', room=self.game.name, data=f'_gold_rush_pay_discard|{self.name}|{player.name}|{self.available_cards[card_index].name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_gold_rush_pay_discard|{self.name}|{player.name}|{self.available_cards[card_index].name}') player.notify_self() self.pending_action = PendingAction.PLAY self.notify_self() @@ -845,7 +844,7 @@ class Player: player.equipment.append(self.game.deck.scrap_pile.pop(-1)) player.notify_self() self.game.notify_all() - self.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Fantasma|{player.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Fantasma|{player.name}') self.pending_action = PendingAction.PLAY self.notify_self() elif 'choose_sventagliata' in self.choose_text: @@ -854,8 +853,8 @@ class Player: player = self.game.get_player_named(self.available_cards[card_index]['name']) player.game.attack(self, og, card_name='Sventagliata') player.game.attack(self, player.name, card_name='Sventagliata') - self.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Sventagliata|{og}') - self.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Sventagliata|{player.name}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Sventagliata|{og}') + G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Sventagliata|{player.name}') self.pending_action = PendingAction.PLAY self.notify_self() elif 'choose_tornado' in self.choose_text: @@ -899,11 +898,11 @@ class Player: self.real_character = self.character self.max_lives = self.character.max_lives + self.role.health_mod self.lives = 2 - self.sio.emit('chat_message', room=self.game.name, data=f'_choose_character|{self.name}|{self.character.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_choose_character|{self.name}|{self.character.name}') self.play_turn(again = True) elif self.game.check_event(ceh.Manette) and self.choose_text == 'choose_manette': self.committed_suit_manette = cs.Suit(card_index) - self.sio.emit('chat_message', room=self.game.name, data=f'_choose_manette|{self.name}|{"♦♣♥♠"[card_index]}') + G.sio.emit('chat_message', room=self.game.name, data=f'_choose_manette|{self.name}|{"♦♣♥♠"[card_index]}') self.pending_action = PendingAction.PLAY self.notify_self() elif self.is_giving_life and self.game.check_event(ce.FratelliDiSangue): @@ -918,7 +917,7 @@ class Player: if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): self.hand.append(self.game.deck.draw()) player.notify_self() - self.sio.emit('chat_message', room=self.game.name, data=f'_fratelli_sangue|{self.name}|{player.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_fratelli_sangue|{self.name}|{player.name}') except: pass self.play_turn(again = True) elif self.is_using_checchino and self.game.check_event(ce.Cecchino): @@ -975,15 +974,15 @@ class Player: elif self.is_drawing and self.game.check_event(ce.Peyote): self.is_drawing = False card = self.game.deck.draw() - self.sio.emit('chat_message', room=self.game.name, data=f"_guess|{self.name}|{self.available_cards[card_index]['icon']}") + G.sio.emit('chat_message', room=self.game.name, data=f"_guess|{self.name}|{self.available_cards[card_index]['icon']}") self.available_cards = [] if card_index == card.suit%2: self.hand.append(card) - self.sio.emit('chat_message', room=self.game.name, data=f"_guess_right|{self.name}") + G.sio.emit('chat_message', room=self.game.name, data=f"_guess_right|{self.name}") self.pending_action = PendingAction.DRAW else: self.game.deck.scrap(card) - self.sio.emit('chat_message', room=self.game.name, data=f"_guess_wrong|{self.name}") + G.sio.emit('chat_message', room=self.game.name, data=f"_guess_wrong|{self.name}") self.pending_action = PendingAction.PLAY self.notify_self() # specifico per personaggio @@ -1051,7 +1050,7 @@ class Player: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f'Did pick {picked}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.mancato_needed -= 1 @@ -1081,7 +1080,7 @@ class Player: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f'Did pick {picked}') - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.mancato_needed -= 1 @@ -1213,34 +1212,34 @@ class Player: self.lives += 1 if not self.character.check(self.game, chd.TequilaJoe) else 2 self.lives = min(self.lives, self.max_lives) self.game.deck.scrap(self.hand.pop(i), True) - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_beer_save|{self.name}') break def take_damage_response(self): self.lives -= 1 - self.sio.emit('hurt', room=self.sid, data=f'') + G.sio.emit('hurt', room=self.sid, data=f'') if self.lives > 0: if self.character.check(self.game, chars.BartCassidy): - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') self.hand.append(self.game.deck.draw(True)) elif self.character.check(self.game, chars.ElGringo) and self.attacker and self.attacker in self.game.get_alive_players() and len(self.attacker.hand) > 0: self.hand.append(self.attacker.hand.pop(randrange(0, len(self.attacker.hand)))) self.hand[-1].reset_card() - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_special_el_gringo|{self.name}|{self.attacker.name}') self.attacker.notify_self() if isinstance(self.attacker, Player) and not self.game.check_event(ce.Lazo): if any((isinstance(c, tvosc.Taglia) for c in self.equipment)): self.attacker.hand.append(self.game.deck.draw(True)) - self.sio.emit('chat_message', room=self.game.name, + G.sio.emit('chat_message', room=self.game.name, data=f'_taglia_reward|{self.name}|{self.attacker.name}') self.attacker.notify_self() if len(self.hand) > 0 and any((isinstance(cd, tvosc.Shotgun) for cd in self.attacker.equipment)): c = self.hand.pop(randrange(0, len(self.hand))) self.game.deck.scrap(c, True) - self.sio.emit('chat_message', room=self.game.name, data=f'_shotgun_scrap|{self.name}|{c.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_shotgun_scrap|{self.name}|{c.name}') if self.attacker and 'gold_rush' in self.game.expansions: if (isinstance(self.attacker, Player)): self.attacker.gold_nuggets += 1 @@ -1285,7 +1284,7 @@ class Player: self.hand.append(self.game.deck.draw(True)) card.use_card(self) print(f'{self.game.name}: {self.name} responded with {card.name}') - self.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card.name}') + G.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card.name}') self.game.deck.scrap(card, True) self.notify_self() self.mancato_needed -= 1 @@ -1448,14 +1447,14 @@ class Player: ##Vendetta## if not forced and self.game.check_event(ce.Vendetta) and self.can_play_vendetta: picked: cs.Card = self.game.deck.pick_and_scrap() - self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.play_turn(can_play_vendetta=False) return ##Don Bell## if not forced and self.character.check(self.game, grch.DonBell) and self.can_play_again_don_bell: picked: cs.Card = self.game.deck.pick_and_scrap() - self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') + G.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}') self.can_play_again_don_bell = False if picked.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]): self.play_turn(can_play_vendetta=False) diff --git a/backend/globals.py b/backend/globals.py new file mode 100644 index 0000000..2c31932 --- /dev/null +++ b/backend/globals.py @@ -0,0 +1,6 @@ + +class G: + sio = None + + def __init__(self): + pass \ No newline at end of file diff --git a/backend/server.py b/backend/server.py index 2873095..f2ac351 100644 --- a/backend/server.py +++ b/backend/server.py @@ -20,10 +20,12 @@ sys.setrecursionlimit(10**6) # this should prevents bots from stopping import logging logging.basicConfig(filename='out.log', level='ERROR') from functools import wraps +from globals import G Metrics.init() sio = socketio.Server(cors_allowed_origins="*") +G.sio = sio static_files={ '/': {'content_type': 'text/html', 'filename': 'index.html'}, @@ -114,7 +116,7 @@ def set_username(sid, username): ses = sio.get_session(sid) if not isinstance(ses, Player): dt = username["discord_token"] if 'discord_token' in username else None - sio.save_session(sid, Player(username["name"], sid, sio, discord_token=dt)) + sio.save_session(sid, Player(username["name"], sid, discord_token=dt)) print(f'{sid} is now {username}') advertise_lobbies() elif ses.game == None or not ses.game.started: @@ -138,7 +140,7 @@ def get_me(sid, room): sio.get_session(sid).game.notify_room() else: dt = room["discord_token"] if 'discord_token' in room else None - sio.save_session(sid, Player('player', sid, sio, discord_token=dt)) + sio.save_session(sid, Player('player', sid, discord_token=dt)) if 'replay' in room and room['replay'] != None: create_room(sid, room['replay']) sid = sio.get_session(sid) @@ -160,9 +162,9 @@ def get_me(sid, room): join_room(sid, room) elif len(de_games) == 1 and de_games[0].started: print('room exists') - if room['username'] != None and any((p.name == room['username'] for p in de_games[0].players if (p.is_bot or (dt != None and p.discord_token == dt)))): + if room['username'] != None and any((p.name == room['username'] for p in de_games[0].players if (p.is_bot or (dt != None and p.discord_token == dt) or p.sid == None))): print('getting inside the bot') - bot = [p for p in de_games[0].players if p.is_bot and p.name == room['username'] ][0] + bot = [p for p in de_games[0].players if (p.is_bot or (dt != None and p.discord_token == dt) or p.sid == None) and p.name == room['username']][0] bot.sid = sid bot.is_bot = False sio.enter_room(sid, de_games[0].name) @@ -225,7 +227,7 @@ def create_room(sid, room_name): room_name += f'_{random.randint(0,100)}' sio.leave_room(sid, 'lobby') sio.enter_room(sid, room_name) - g = Game(room_name, sio) + g = Game(room_name) g.add_player(sio.get_session(sid)) if room_name in blacklist: g.is_hidden = True @@ -436,12 +438,12 @@ def chat_message(sid, msg, pl=None): if '/addbot' in msg and not ses.game.started: if len(msg.split()) > 1: # for _ in range(int(msg.split()[1])): - # ses.game.add_player(Player(f'AI_{random.randint(0,1000)}', 'bot', sio, bot=True)) + # ses.game.add_player(Player(f'AI_{random.randint(0,1000)}', 'bot', bot=True)) sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'Only 1 bot at the time'}) else: - bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True) + bot = Player(f'AI_{random.randint(0,10)}', 'bot', bot=True) while any((p for p in ses.game.players if p.name == bot.name)): - bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True) + bot = Player(f'AI_{random.randint(0,10)}', 'bot', bot=True) ses.game.add_player(bot) bot.bot_spin() return @@ -763,6 +765,7 @@ def discord_auth(sid, data): if res.status_code == 200: sio.emit('discord_auth_succ', room=sid, data=res.json()) + def pool_metrics(): sio.sleep(60) Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) @@ -790,7 +793,23 @@ class CustomProxyFix(object): discord_ci = '1059452581027532880' discord_cs = 'Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu' +import pickle +def save_games(): + sio.sleep(2) + with open('games.pickle', 'wb') as f: + pickle.dump([g for g in games if g.started], f) + save_games() if __name__ == '__main__': + if os.path.exists('games.pickle'): + with open('games.pickle', 'rb') as file: + games = pickle.load(file) + for g in games: + for p in g.players: + if p.sid != 'bot': + p.sid = None + else: + sio.start_background_task(p.bot_spin) + sio.start_background_task(save_games) sio.start_background_task(pool_metrics) eventlet.wsgi.server(eventlet.listen(('', 5001)), CustomProxyFix(app)) diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py index e69de29..e79239c 100644 --- a/backend/tests/__init__.py +++ b/backend/tests/__init__.py @@ -0,0 +1,3 @@ +from tests.dummy_socket import DummySocket +from globals import G +G.sio = DummySocket() \ No newline at end of file diff --git a/backend/tests/cards_test.py b/backend/tests/cards_test.py index 115a4f9..67732f7 100644 --- a/backend/tests/cards_test.py +++ b/backend/tests/cards_test.py @@ -1,16 +1,15 @@ from random import randint from bang.characters import Character from bang.cards import * -from tests.dummy_socket import DummySocket from bang.deck import Deck from bang.game import Game from bang.players import Player, PendingAction # test card Barile def test_barile(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -43,9 +42,9 @@ def test_barile(): #test card Volcanic def test_volcanic(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -67,9 +66,9 @@ def test_volcanic(): # test card Dinamite def test_dinamite(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -103,9 +102,9 @@ def test_dinamite(): # test mirino def test_mirino(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(4)] for p in ps: g.add_player(p) g.start_game() @@ -121,9 +120,9 @@ def test_mirino(): # test mustang def test_mustang(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -139,9 +138,9 @@ def test_mustang(): # test Prigione def test_prigione(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(4)] for p in ps: g.add_player(p) g.start_game() @@ -171,9 +170,9 @@ def test_prigione(): # test all weapons ranges def test_all_weapons(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(4)] for p in ps: g.add_player(p) g.start_game() @@ -195,9 +194,9 @@ def test_all_weapons(): # test bang def test_bang(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -214,9 +213,9 @@ def test_bang(): # test birra def test_birra_2p(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -231,9 +230,9 @@ def test_birra_2p(): # test birra def test_birra_3p(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -262,9 +261,9 @@ def test_birra_3p(): # test CatBalou def test_catbalou(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -282,9 +281,9 @@ def test_catbalou(): # test Diligenza def test_diligenza(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(4)] for p in ps: g.add_player(p) g.start_game() @@ -298,9 +297,9 @@ def test_diligenza(): # test Duello def test_duello(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -326,9 +325,9 @@ def test_duello(): # test Emporio def test_emporio(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(7)] for p in ps: g.add_player(p) g.start_game() @@ -352,9 +351,9 @@ def test_emporio(): # test Gatling def test_gatling(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(7)] for p in ps: g.add_player(p) g.start_game() @@ -386,9 +385,9 @@ def test_gatling(): # test Indiani def test_indiani(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(7)] for p in ps: g.add_player(p) g.start_game() @@ -420,9 +419,9 @@ def test_indiani(): # test Mancato def test_mancato(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -442,9 +441,9 @@ def test_mancato(): # test Panico def test_panico(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -461,9 +460,9 @@ def test_panico(): # test Saloon def test_saloon(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(8)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(8)] for p in ps: g.add_player(p) g.start_game() @@ -480,9 +479,9 @@ def test_saloon(): # test WellsFargo def test_wells_fargo(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(4)] for p in ps: g.add_player(p) g.start_game() diff --git a/backend/tests/character_test.py b/backend/tests/character_test.py index daaf109..3963f97 100644 --- a/backend/tests/character_test.py +++ b/backend/tests/character_test.py @@ -1,15 +1,14 @@ from random import randint from bang.characters import * -from tests.dummy_socket import DummySocket from bang.deck import Deck from bang.game import Game from bang.players import Player, PendingAction from bang.cards import * def test_bartcassidy(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -29,9 +28,9 @@ def test_bartcassidy(): assert len(g.players[(g.turn+1)%2].hand) == 1 def test_blackjack(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(1)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(1)] for p in ps: g.add_player(p) g.start_game() @@ -62,9 +61,9 @@ def test_blackjack(): g.players[g.turn].end_turn() def test_calamityjanet(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -89,9 +88,9 @@ def test_calamityjanet(): assert g.players[(g.turn+1)%2].lives == 1 def test_ElGringo(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -112,9 +111,9 @@ def test_ElGringo(): assert len(g.players[g.turn].hand) == 0 def test_JesseJones(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -133,9 +132,9 @@ def test_JesseJones(): assert len(g.players[g.turn].hand) == 2 def test_Jourdonnais(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -153,9 +152,9 @@ def test_Jourdonnais(): assert g.players[(g.turn+1)%2].pending_action == PendingAction.PICK def test_KitCarlson(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -176,9 +175,9 @@ def test_KitCarlson(): assert g.players[g.turn].pending_action == PendingAction.PLAY def test_LuckyDuke(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -198,9 +197,9 @@ def test_LuckyDuke(): assert g.players[g.turn].pending_action == PendingAction.DRAW def test_PaulRegret(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -214,9 +213,9 @@ def test_PaulRegret(): assert pls[0]['dist'] > g.players[0].get_sight() def test_PedroRamirez(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -237,9 +236,9 @@ def test_PedroRamirez(): assert isinstance(g.players[g.turn].hand[0], Bang) def test_RoseDoolan(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -256,9 +255,9 @@ def test_RoseDoolan(): assert pls[0]['dist'] <= g.players[1].get_sight() def test_SidKetchum(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -278,9 +277,9 @@ def test_SidKetchum(): assert g.players[g.turn].lives == 2 def test_SlabTheKiller(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -301,9 +300,9 @@ def test_SlabTheKiller(): assert g.players[(g.turn+1)%2].lives == 3 def test_SuzyLafayette(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -318,9 +317,9 @@ def test_SuzyLafayette(): g.players[g.turn].end_turn() def test_VultureSam(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -341,9 +340,9 @@ def test_VultureSam(): return def test_WillyTheKid(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() diff --git a/backend/tests/deck_test.py b/backend/tests/deck_test.py index 0f7bf2d..b89fec8 100644 --- a/backend/tests/deck_test.py +++ b/backend/tests/deck_test.py @@ -3,7 +3,7 @@ from bang.deck import Deck from bang.game import Game def test_card_flip(): - g = Game('test', DummySocket()) + g = Game('test') g.deck = Deck(g) l = len(g.deck.cards) assert g.deck.pick_and_scrap() != None @@ -11,7 +11,7 @@ def test_card_flip(): assert len(g.deck.scrap_pile) == 1 def test_draw(): - g = Game('test', DummySocket()) + g = Game('test') g.deck = Deck(g) l = len(g.deck.cards) assert g.deck.draw(True) != None @@ -19,7 +19,7 @@ def test_draw(): assert len(g.deck.scrap_pile) == 0 def test_reshuffle(): - g = Game('test', DummySocket()) + g = Game('test') g.deck = Deck(g) l = len(g.deck.cards) for i in range(80): @@ -28,7 +28,7 @@ def test_reshuffle(): assert len(g.deck.scrap_pile) == 1 def test_draw_from_scrap(): - g = Game('test', DummySocket()) + g = Game('test') g.deck = Deck(g) l = len(g.deck.cards) assert g.deck.pick_and_scrap() != None diff --git a/backend/tests/game_test.py b/backend/tests/game_test.py index 57cdd0e..fcb4ec6 100644 --- a/backend/tests/game_test.py +++ b/backend/tests/game_test.py @@ -1,4 +1,3 @@ -from tests.dummy_socket import DummySocket from bang.deck import Deck from bang.game import Game from bang.players import Player, PendingAction @@ -7,13 +6,13 @@ from bang.cards import * # test that game can start def test_game_start(): - sio = DummySocket() - g = Game('test', sio) - p1 = Player('p1', 'p1', sio) + + g = Game('test') + p1 = Player('p1', 'p1') g.add_player(p1) - p2 = Player('p2', 'p2', sio) + p2 = Player('p2', 'p2') g.add_player(p2) - p3 = Player('p3', 'p3', sio) + p3 = Player('p3', 'p3') g.add_player(p3) assert p1.role == None assert p2.role == None @@ -37,20 +36,20 @@ def test_game_start(): # test that dodge_city is added to games with more than 8 players def test_dodge_city(): - sio = DummySocket() - g = Game('test', sio) + + g = Game('test') for i in range(9): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) assert 'dodge_city' in g.expansions # test that a game with 2 players has only renegade as role def test_renegade_only(): - sio = DummySocket() - g = Game('test', sio) - p1 = Player('p1', 'p1', sio) + + g = Game('test') + p1 = Player('p1', 'p1') g.add_player(p1) - p2 = Player('p2', 'p2', sio) + p2 = Player('p2', 'p2') g.add_player(p2) g.start_game() assert isinstance(g.players[0].role, Renegade) @@ -58,10 +57,10 @@ def test_renegade_only(): # test that a game with 3 player has Renegade, Vice and Outlaw as roles def test_renegade_vice_outlaw(): - sio = DummySocket() - g = Game('test', sio) + + g = Game('test') for i in range(3): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() roles = {p.role.name for p in g.players} @@ -69,10 +68,10 @@ def test_renegade_vice_outlaw(): # test that a game with 4 players has all roles except the deputy def test_4_players_roles(): - sio = DummySocket() - g = Game('test', sio) + + g = Game('test') for i in range(4): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() roles = {p.role.name for p in g.players} @@ -80,10 +79,10 @@ def test_4_players_roles(): # test that a game with 5 players has all roles def test_5_players_roles(): - sio = DummySocket() - g = Game('test', sio) + + g = Game('test') for i in range(5): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() roles = {p.role.name for p in g.players} diff --git a/backend/tests/roles_test.py b/backend/tests/roles_test.py index ab32b4f..9f2c34f 100644 --- a/backend/tests/roles_test.py +++ b/backend/tests/roles_test.py @@ -1,5 +1,4 @@ from bang.characters import Character -from tests.dummy_socket import DummySocket from bang.deck import Deck from bang.game import Game from bang.players import Player, PendingAction @@ -8,10 +7,9 @@ from bang.cards import * # test that a game with 3 player the deputy kills renegade and wins def test_3p_deputy_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(3): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -33,10 +31,9 @@ def test_3p_deputy_win(): # test that a game with 3 player the renegade kills the outlaw and wins def test_3p_renegade_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(3): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -60,10 +57,9 @@ def test_3p_renegade_win(): # test that a game with 3 player the outlaw kills the deputy and wins def test_3p_outlaw_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(3): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -87,10 +83,9 @@ def test_3p_outlaw_win(): # test that a game with 4 player the outlaw kills the sheriff and win def test_4p_outlaw_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(4): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -116,10 +111,9 @@ def test_4p_outlaw_win(): # test that a game with 5 player the renegade kills all the other players and wins def test_5p_renegade_gatling_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(5): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -148,10 +142,9 @@ def test_5p_renegade_gatling_win(): # test that a game with 5 player the renegade kills all the other players and wins def test_5p_renegade_indiani_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(5): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: @@ -180,10 +173,9 @@ def test_5p_renegade_indiani_win(): # test that a game with 5 player the renegade kills the sheriff but it isn't the last alive player and the outlaws wins def test_5p_outlaw_death_win(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') for i in range(5): - p = Player(f'p{i}', f'p{i}', sio) + p = Player(f'p{i}', f'p{i}') g.add_player(p) g.start_game() for p in g.players: diff --git a/backend/tests/valley_of_shadows_test.py b/backend/tests/valley_of_shadows_test.py index 8749dc1..00d7a60 100644 --- a/backend/tests/valley_of_shadows_test.py +++ b/backend/tests/valley_of_shadows_test.py @@ -1,7 +1,6 @@ from random import randint from bang.characters import Character from bang.expansions.the_valley_of_shadows.cards import * -from tests.dummy_socket import DummySocket from bang.deck import Deck from bang.game import Game from bang.players import Player, PendingAction @@ -9,9 +8,8 @@ import bang.cards as cs # test UltimoGiro def test_ultimo_giro(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -28,9 +26,8 @@ def test_ultimo_giro(): # test Tomahawk def test_tomahawk(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(6)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(6)] for p in ps: g.add_player(p) g.start_game() @@ -48,9 +45,8 @@ def test_tomahawk(): # test Fantasma def test_fantasma(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -73,9 +69,8 @@ def test_fantasma(): # test SerpenteASonagli def test_serpente_a_sonagli(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -107,10 +102,9 @@ def test_serpente_a_sonagli(): # test RitornoDiFiamma def test_ritorno_di_fiamma(): - sio = DummySocket() - g = Game('test', sio) + g = Game('test') g.expansions = ['the_valley_of_shadows'] - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -144,9 +138,8 @@ def test_ritorno_di_fiamma(): # test RitornoDiFiamma with gatling def test_ritorno_di_fiamma_gatling(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] g.expansions = ['the_valley_of_shadows'] for p in ps: g.add_player(p) @@ -177,9 +170,8 @@ def test_ritorno_di_fiamma_gatling(): # test Taglia def test_taglia(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(3)] for p in ps: g.add_player(p) g.start_game() @@ -201,9 +193,8 @@ def test_taglia(): # test Bandidos def test_bandidos(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -233,9 +224,8 @@ def test_bandidos(): # test Poker def test_poker(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() @@ -265,9 +255,8 @@ def test_poker(): # test Tornado def test_tornado(): - sio = DummySocket() - g = Game('test', sio) - ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)] + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] for p in ps: g.add_player(p) g.start_game() From 5eafa4fa1865171619938f142ad6a1c75718a39e Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Fri, 20 Jan 2023 21:03:39 +0000 Subject: [PATCH 02/27] remove reference to sio in replay --- backend/bang/game.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index ffbcb4f..d262078 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -118,7 +118,6 @@ class Game: self.notify_room() def replay(self, log, speed=1.0, fast_forward = -1): - from tests.dummy_socket import DummySocket self.players = [] self.is_hidden = True self.is_replay = True @@ -135,7 +134,7 @@ class Game: self.expansions = json.loads(cmd[4].replace("'",'"')) pnames = json.loads(cmd[3].replace("'",'"')) for p in pnames: - self.add_player(pl.Player(p, p, DummySocket(G.sio), bot=False)) + self.add_player(pl.Player(p, 'a', bot=False)) continue if cmd[1] == 'start_game': self.start_game(int(cmd[2])) From babc4f3f484174edb07726891259a22a578a37d0 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 10:56:11 +0000 Subject: [PATCH 03/27] start bot disconnect on restart --- backend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index f2ac351..a0e44c2 100644 --- a/backend/server.py +++ b/backend/server.py @@ -807,7 +807,7 @@ if __name__ == '__main__': for g in games: for p in g.players: if p.sid != 'bot': - p.sid = None + sio.start_background_task(p.disconnect) else: sio.start_background_task(p.bot_spin) sio.start_background_task(save_games) From 5d7f8339169902ae9550dd06f920eac0b43d63e1 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 11:07:30 +0000 Subject: [PATCH 04/27] prevent debugger disconnect --- backend/bang/players.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/bang/players.py b/backend/bang/players.py index 0b077e3..82e1ecc 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -150,6 +150,7 @@ class Player: print(f'I {self.name} joined {self.game}') def disconnect(self): + if self.is_admin() and self.game.debug: return False return self.game.handle_disconnect(self) def set_role(self, role: r.Role): From d7187dbc6196eeb5c1e782e5c89167a15db66ba6 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 11:50:11 +0000 Subject: [PATCH 05/27] add reset button --- backend/server.py | 9 +++++++++ frontend/src/components/Status.vue | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/backend/server.py b/backend/server.py index a0e44c2..33794c7 100644 --- a/backend/server.py +++ b/backend/server.py @@ -311,6 +311,15 @@ def kick(sid, data): if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): sio.emit('kicked', room=data['sid']) +@sio.event +@bang_handler +def reset(sid, data): + global games + if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): + for g in games: + sio.emit('kicked', room=g.name) + games = [] + @sio.event @bang_handler def hide_toogle(sid, data): diff --git a/frontend/src/components/Status.vue b/frontend/src/components/Status.vue index 48f1fb7..f598b57 100644 --- a/frontend/src/components/Status.vue +++ b/frontend/src/components/Status.vue @@ -3,6 +3,7 @@

PewPew! Server Status

Rooms {{rooms.length}}

+
  • name: {{r.name}}

    @@ -60,6 +61,10 @@ export default { refresh(){ this.$socket.emit('get_all_rooms', this.deploy_key) }, + reset(){ + if (confirm('ARE YOU SURE? KICK EVERYONE AND RESET LOBBIES?')) + this.$socket.emit('reset', this.deploy_key) + }, hide(room_name){ this.$socket.emit('hide_toogle', {'key':this.deploy_key, 'room':room_name}) setTimeout((()=>{ From 8e745fa7963bf74c20a0e381bbc76c292f346860 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 13:03:00 +0000 Subject: [PATCH 06/27] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 566ac6e..d81a0a5 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,4 @@ frontend/package-lock.json bang-workspace.code-workspace .vscode/ +backend/games.pickle From ec48007377931dffb1f454b0118749956647b06f Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 13:15:20 +0000 Subject: [PATCH 07/27] add save lock --- backend/server.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/server.py b/backend/server.py index 33794c7..cca3e2a 100644 --- a/backend/server.py +++ b/backend/server.py @@ -56,15 +56,19 @@ def send_to_debug(error): if g.debug or any((p.is_admin() for p in g.players)): sio.emit('chat_message', room=g.name, data={'color': f'red','text':json.dumps({'ERROR':error}), 'type':'json'}) +save_lock = False def bang_handler(func): @wraps(func) def wrapper_func(*args, **kwargs): + global save_lock + save_lock = True try: func(*args, **kwargs) except Exception as e: logging.exception(e) print(traceback.format_exc()) send_to_debug(traceback.format_exc()) + save_lock = False return wrapper_func def advertise_lobbies(): @@ -454,7 +458,7 @@ def chat_message(sid, msg, pl=None): while any((p for p in ses.game.players if p.name == bot.name)): bot = Player(f'AI_{random.randint(0,10)}', 'bot', bot=True) ses.game.add_player(bot) - bot.bot_spin() + sio.start_background_task(bot.bot_spin) return if '/replay' in msg and not '/replayspeed' in msg and not '/replaypov' in msg: _cmd = msg.split() @@ -804,9 +808,10 @@ discord_ci = '1059452581027532880' discord_cs = 'Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu' import pickle def save_games(): - sio.sleep(2) - with open('games.pickle', 'wb') as f: - pickle.dump([g for g in games if g.started], f) + global save_lock + if not save_lock: + with open('games.pickle', 'wb') as f: + pickle.dump([g for g in games if g.started], f) save_games() if __name__ == '__main__': From 13ab981c5998deade9fedae80fa5e31d6f7078dc Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 13:40:41 +0000 Subject: [PATCH 08/27] introduce back delay --- backend/server.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/backend/server.py b/backend/server.py index cca3e2a..e58d805 100644 --- a/backend/server.py +++ b/backend/server.py @@ -810,20 +810,24 @@ import pickle def save_games(): global save_lock if not save_lock: + sio.sleep(2) with open('games.pickle', 'wb') as f: pickle.dump([g for g in games if g.started], f) save_games() if __name__ == '__main__': if os.path.exists('games.pickle'): - with open('games.pickle', 'rb') as file: - games = pickle.load(file) - for g in games: - for p in g.players: - if p.sid != 'bot': - sio.start_background_task(p.disconnect) - else: - sio.start_background_task(p.bot_spin) + try: + with open('games.pickle', 'rb') as file: + games = pickle.load(file) + for g in games: + for p in g.players: + if p.sid != 'bot': + sio.start_background_task(p.disconnect) + else: + sio.start_background_task(p.bot_spin) + except: + pass sio.start_background_task(save_games) sio.start_background_task(pool_metrics) eventlet.wsgi.server(eventlet.listen(('', 5001)), CustomProxyFix(app)) From 699c976c759dbd5df7298728a07a58880e152eef Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 14:03:27 +0000 Subject: [PATCH 09/27] fix replays --- backend/bang/game.py | 5 +++-- backend/server.py | 9 +++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index d262078..3d3659b 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -82,6 +82,7 @@ class Game: self.rng = random.Random() self.rpc_log = [] self.is_replay = False + self.replay_speed = 1 def shuffle_players(self): if not self.started: @@ -173,7 +174,7 @@ class Game: if i == fast_forward: self.replay_speed = 1.0 self.notify_room() - eventlet.sleep(max(self.replay_speed, 0.1)) + eventlet.sleep(max(self.replay_speed, 0.001)) eventlet.sleep(6) if self.is_replay: self.reset() @@ -835,7 +836,7 @@ class Game: return [p for p in self.players if p.is_dead and (include_ghosts or not p.is_ghost)] def notify_all(self): - if self.started: + if self.started and self.replay_speed > 0: data = [{ 'name': p.name, 'ncards': len(p.hand), diff --git a/backend/server.py b/backend/server.py index e58d805..21e0d16 100644 --- a/backend/server.py +++ b/backend/server.py @@ -159,7 +159,7 @@ def get_me(sid, room): if 'ffw' not in room: sid.game.replay(log) else: - sid.game.replay(log, speed=0.1, fast_forward=int(room['ffw'])) + sid.game.replay(log, speed=0, fast_forward=int(room['ffw'])) return de_games = [g for g in games if g.name == room['name']] if len(de_games) == 1 and not de_games[0].started: @@ -471,7 +471,7 @@ def chat_message(sid, msg, pl=None): ses.game.replay(log) if len(_cmd) == 3: line = int(_cmd[2]) - ses.game.replay(log, speed=0.1, fast_forward=line) + ses.game.replay(log, speed=0, fast_forward=line) return if '/replayspeed' in msg: _cmd = msg.split() @@ -484,11 +484,8 @@ def chat_message(sid, msg, pl=None): name = ' '.join(_cmd[1:]) for p in ses.game.players: if p.sid == ses.sid: - from tests.dummy_socket import DummySocket - p.sio = DummySocket() p.sid = '' pl = ses.game.get_player_named(name) - pl.sio = sio pl.sid = ses.sid pl.set_role(pl.role) pl.notify_self() @@ -812,7 +809,7 @@ def save_games(): if not save_lock: sio.sleep(2) with open('games.pickle', 'wb') as f: - pickle.dump([g for g in games if g.started], f) + pickle.dump([g for g in games if g.started and not g.is_replay], f) save_games() if __name__ == '__main__': From 461dfb884138c0fd124d391fbd8f2c16682616f9 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 14:16:48 +0000 Subject: [PATCH 10/27] fix debug disconnect --- backend/bang/game.py | 2 ++ backend/bang/players.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index 3d3659b..8548b95 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -125,6 +125,8 @@ class Game: self.replay_speed = speed for i in range(len(log)-1): print('replay:', i, 'of', len(log)-3, '->', log[i]) + if len(self.spectators) == 0: + break if (log[i] == "@@@"): eventlet.sleep(10) if self.is_replay: diff --git a/backend/bang/players.py b/backend/bang/players.py index 82e1ecc..da332a5 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -150,7 +150,7 @@ class Player: print(f'I {self.name} joined {self.game}') def disconnect(self): - if self.is_admin() and self.game.debug: return False + if self.is_admin() and self.game.debug and self.game.started: return False return self.game.handle_disconnect(self) def set_role(self, role: r.Role): From 1b263adc8069262324fa4cb648ca4e631e168b26 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 14:28:34 +0000 Subject: [PATCH 11/27] fix players becoming bots --- backend/bang/game.py | 2 +- backend/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index 8548b95..2f2cdca 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -678,7 +678,7 @@ class Game: player.was_player = False if len(player.available_characters) > 0: player.set_available_character(player.available_characters) - player.bot_spin() + G.sio.start_background_task(player.bot_spin) else: self.player_death(player=player, disconnected=True) # else: diff --git a/backend/server.py b/backend/server.py index 21e0d16..07bfbe4 100644 --- a/backend/server.py +++ b/backend/server.py @@ -678,7 +678,7 @@ def chat_message(sid, msg, pl=None): ses.is_bot = not ses.is_bot if (ses.is_bot): ses.was_player = True - ses.bot_spin() + sio.start_background_task(ses.bot_spin) elif '/arcadekick' in msg and ses.game.started: if not any((p.pending_action != PendingAction.WAIT for p in ses.game.players)): sio.emit('chat_message', room=ses.game.name, data={'color': f'','text':f'KICKING THE ARCADE CABINET'}) From 28a093d337a1ccf8d1a2bcfd71e592adf697bd0e Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 14:41:53 +0000 Subject: [PATCH 12/27] remove recursion --- backend/server.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/backend/server.py b/backend/server.py index 07bfbe4..e2be5a4 100644 --- a/backend/server.py +++ b/backend/server.py @@ -27,6 +27,10 @@ Metrics.init() sio = socketio.Server(cors_allowed_origins="*") G.sio = sio +import faulthandler + +faulthandler.enable() + static_files={ '/': {'content_type': 'text/html', 'filename': 'index.html'}, '/game': {'content_type': 'text/html', 'filename': 'index.html'}, @@ -73,7 +77,7 @@ def bang_handler(func): def advertise_lobbies(): sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if not g.started and len(g.players) < 10 and not g.is_hidden]) - sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if g.started and not g.is_hidden]) + sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if g.started and not g.is_hidden and len(g.players) > 0]) Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) Metrics.send_metric('online_players', points=[online_players]) @@ -777,10 +781,10 @@ def discord_auth(sid, data): def pool_metrics(): - sio.sleep(60) - Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) - Metrics.send_metric('online_players', points=[online_players]) - pool_metrics() + while True: + sio.sleep(60) + Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) + Metrics.send_metric('online_players', points=[online_players]) import urllib.parse class CustomProxyFix(object): @@ -806,11 +810,11 @@ discord_cs = 'Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu' import pickle def save_games(): global save_lock - if not save_lock: - sio.sleep(2) - with open('games.pickle', 'wb') as f: - pickle.dump([g for g in games if g.started and not g.is_replay], f) - save_games() + while True: + if not save_lock: + sio.sleep(2) + with open('games.pickle', 'wb') as f: + pickle.dump([g for g in games if g.started and not g.is_replay], f) if __name__ == '__main__': if os.path.exists('games.pickle'): From ca342f01067def0edde9bb4921041dd54f7a2695 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 14:53:08 +0000 Subject: [PATCH 13/27] prevent infinite loop --- backend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index e2be5a4..56d9a6a 100644 --- a/backend/server.py +++ b/backend/server.py @@ -811,8 +811,8 @@ import pickle def save_games(): global save_lock while True: + sio.sleep(2) if not save_lock: - sio.sleep(2) with open('games.pickle', 'wb') as f: pickle.dump([g for g in games if g.started and not g.is_replay], f) From 08c81c4a2112e6959d6526bec3c3f21b9f12de1f Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 21 Jan 2023 16:13:03 +0000 Subject: [PATCH 14/27] fix error if no player is alive --- frontend/src/components/Lobby.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index f8a6d25..569ec1d 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -244,7 +244,11 @@ export default { return '' }, isRoomOwner() { - return this.players.length > 0 && this.players.filter(x => !x.is_bot)[0].name == this.username + if (this.players.length > 0){ + let pls = this.players.filter(x => !x.is_bot) + return pls.length > 0 && pls[0].name == this.username + } + return false }, startGameCard() { if (!this.started && this.players.length > 2 && this.isRoomOwner) { @@ -319,12 +323,13 @@ export default { }, getPlayerCard(player) { let icon = '' - let owner = this.players.filter(x => !x.is_bot)[0]; + let nonBots = this.players.filter(x => !x.is_bot) + let isOwner = nonBots.length > 0 && nonBots[0].name == player.name; if (!this.started) icon = '🤠' else icon = player.ready !== undefined ? ((player.ready)?'👍': '🤔') : (player.is_sheriff ? '⭐' : player.icon) return { name: player.name, - number: ((this.username == player.name) ? this.$t('you') : (owner.name == player.name) ? this.$t('owner') :'') + (player.dist ? `${player.dist}⛰` : ''), + number: ((this.username == player.name) ? this.$t('you') : isOwner ? this.$t('owner') :'') + (player.dist ? `${player.dist}⛰` : ''), icon: icon, is_character: true, avatar: player.avatar, From 867a07934175585694100fb49c29976a4b27a6b2 Mon Sep 17 00:00:00 2001 From: GM Date: Sat, 21 Jan 2023 17:07:48 +0000 Subject: [PATCH 15/27] fix bot logic with The Judge and green cards --- backend/bang/players.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/bang/players.py b/backend/bang/players.py index da332a5..2f8d72b 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -319,7 +319,7 @@ class Player: self.game.rpc_log.append(f'{self.name};draw;') self.draw('') elif self.pending_action == PendingAction.PLAY: - non_blocked_cards = [card for card in self.hand if (not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette)] + non_blocked_cards = [card for card in self.hand if (not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette) and (not self.game.check_event(ce.IlGiudice) or not card.usable_next_turn) ] equippables = [c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not c.need_target and not any((type(c) == type(x) and not (c.is_weapon and c.must_be_used) for x in self.equipment))] misc = [c for c in non_blocked_cards if not c.need_target and (isinstance(c, cs.WellsFargo) or isinstance(c, cs.Indiani) or isinstance(c, cs.Gatling) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or ((isinstance(c, cs.Birra) and self.lives < self.max_lives or c.must_be_used) and not self.game.check_event(ceh.IlReverendo)) or (c.need_with and len(self.hand) > 1 and not (isinstance(c, csd.Whisky) and self.lives == self.max_lives))) and not (not c.can_be_used_now and self.game.check_event(ce.IlGiudice)) and not c.is_equipment] From e2b78a37e9155a4be139029f36685611342f656c Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 22 Jan 2023 11:24:43 +0000 Subject: [PATCH 16/27] block disconnect only while with debugger --- backend/bang/players.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/bang/players.py b/backend/bang/players.py index 2f8d72b..b50222b 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -13,10 +13,10 @@ import bang.expansions.high_noon.card_events as ceh import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.characters as grch import bang.expansions.the_valley_of_shadows.cards as tvosc -import eventlet from typing import List from metrics import Metrics from globals import G +import sys robot_pictures = [ 'https://i.imgur.com/40rAFIb.jpg', @@ -150,7 +150,7 @@ class Player: print(f'I {self.name} joined {self.game}') def disconnect(self): - if self.is_admin() and self.game.debug and self.game.started: return False + if self.is_admin() and self.game.debug and self.game.started and getattr(sys, 'gettrace', None)(): return False return self.game.handle_disconnect(self) def set_role(self, role: r.Role): From fee3171e8a0ebcef0769dd84648c051524f5e1c2 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 22 Jan 2023 11:44:55 +0000 Subject: [PATCH 17/27] reduce times that the player is forced to change name --- backend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index 56d9a6a..bc56a63 100644 --- a/backend/server.py +++ b/backend/server.py @@ -203,7 +203,7 @@ def get_me(sid, room): sio.emit('me', data={'error':'Wrong password/Cannot connect'}, room=sid) else: sio.emit('me', data=sio.get_session(sid).name, room=sid) - if room['username'] == None or any((p.name == room['username'] for p in sio.get_session(sid).game.players)): + if room['username'] == None or any((p.name == room['username'] for p in sio.get_session(sid).game.players if not ((dt != None and p.discord_token == dt) or p.sid == None))): sio.emit('change_username', room=sid) else: sio.emit('chat_message', room=sio.get_session(sid).game.name, data=f"_change_username|{sio.get_session(sid).name}|{room['username']}") From 87dcbedc295b43e7a16b125e7379a96e05963bc1 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 22 Jan 2023 11:52:34 +0000 Subject: [PATCH 18/27] don't save hidden games --- backend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index bc56a63..2c5a2b6 100644 --- a/backend/server.py +++ b/backend/server.py @@ -814,7 +814,7 @@ def save_games(): sio.sleep(2) if not save_lock: with open('games.pickle', 'wb') as f: - pickle.dump([g for g in games if g.started and not g.is_replay], f) + pickle.dump([g for g in games if g.started and not g.is_replay and not g.is_hidden], f) if __name__ == '__main__': if os.path.exists('games.pickle'): From a5e827c88204110389302e98f10460f45b294aab Mon Sep 17 00:00:00 2001 From: GM Date: Sun, 22 Jan 2023 18:35:36 +0000 Subject: [PATCH 19/27] fix name desc for Players - frontend --- frontend/src/components/Lobby.vue | 1 + frontend/src/components/Menu.vue | 1 + frontend/src/components/Player.vue | 3 +++ 3 files changed, 5 insertions(+) diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index 569ec1d..fc72247 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -333,6 +333,7 @@ export default { icon: icon, is_character: true, avatar: player.avatar, + is_player: true } }, startGame() { diff --git a/frontend/src/components/Menu.vue b/frontend/src/components/Menu.vue index 9ac981d..479042c 100644 --- a/frontend/src/components/Menu.vue +++ b/frontend/src/components/Menu.vue @@ -92,6 +92,7 @@ export default { icon: '🤠', is_character: true, avatar: this.discordPic, + is_player: true } }, version() { diff --git a/frontend/src/components/Player.vue b/frontend/src/components/Player.vue index 58e8c03..064879f 100644 --- a/frontend/src/components/Player.vue +++ b/frontend/src/components/Player.vue @@ -245,6 +245,7 @@ export default { icon: this.noStar ? player.icon : player.is_sheriff ? '⭐' : '🤠', avatar: player.avatar, is_character: true, + is_player: true }}) return vis }, @@ -267,6 +268,7 @@ export default { alt_text: Array(player.lives+1).join('❤️')+Array(player.max_lives-player.lives+1).join('💀'), avatar: player.avatar, is_character: true, + is_player: true }}) if (this.card_against && this.card_against.can_target_self) { vis.push({ @@ -274,6 +276,7 @@ export default { number: 0, icon: this.$t('you'), is_character: true, + is_player: true }) } return vis From ee7e02a525606522062a2250c2d35c11f87457a2 Mon Sep 17 00:00:00 2001 From: GM Date: Sun, 22 Jan 2023 18:36:14 +0000 Subject: [PATCH 20/27] fix deadman and fantasma --- backend/bang/game.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/bang/game.py b/backend/bang/game.py index 2f2cdca..cef7972 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -14,6 +14,7 @@ import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.high_noon.card_events as ceh import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.characters as grch +import bang.expansions.the_valley_of_shadows.cards as tvosc from metrics import Metrics from globals import G @@ -566,6 +567,11 @@ class Game: pl.lives = 2 pl.hand.append(self.deck.draw()) pl.hand.append(self.deck.draw()) + if any((True for c in pl.equipment if isinstance(c, tvosc.Fantasma))): + for c in pl.equipment: + if isinstance(c, tvosc.Fantasma): + self.deck.scrap(pl.equipment.pop(c)) + break pl.notify_self() elif self.check_event(ceh.CittaFantasma) or self.players[self.turn].is_ghost: print(f'{self.name}: {self.players[self.turn]} is dead, event ghost') From 292fbe4dde0e0768e6cc5b755c196c8a4e80bbf6 Mon Sep 17 00:00:00 2001 From: GM Date: Sun, 22 Jan 2023 20:51:44 +0000 Subject: [PATCH 21/27] volume save --- Dockerfile | 1 + backend/server.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 34a6cc6..1080c65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,6 @@ WORKDIR /dist EXPOSE 5001 ENV PATH=/root/.local/bin:${PATH} +VOLUME /code/save ENTRYPOINT ["python", "/dist/server.py"] diff --git a/backend/server.py b/backend/server.py index 2c5a2b6..d726ab2 100644 --- a/backend/server.py +++ b/backend/server.py @@ -813,13 +813,13 @@ def save_games(): while True: sio.sleep(2) if not save_lock: - with open('games.pickle', 'wb') as f: + with open('./save/games.pickle', 'wb') as f: pickle.dump([g for g in games if g.started and not g.is_replay and not g.is_hidden], f) if __name__ == '__main__': - if os.path.exists('games.pickle'): + if os.path.exists('./save/games.pickle'): try: - with open('games.pickle', 'rb') as file: + with open('./save/games.pickle', 'rb') as file: games = pickle.load(file) for g in games: for p in g.players: From a7ec4935e0042317294ed6b5ee034e1e11ff0adf Mon Sep 17 00:00:00 2001 From: GM Date: Sun, 22 Jan 2023 21:17:18 +0000 Subject: [PATCH 22/27] fix folder --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1080c65..3a96222 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,9 +20,10 @@ COPY --from=pybuilder /code /dist # copy the frontend static files from the builder COPY --from=builder ./dist /dist/ WORKDIR /dist +RUN mkdir save EXPOSE 5001 ENV PATH=/root/.local/bin:${PATH} -VOLUME /code/save +VOLUME /dist/save ENTRYPOINT ["python", "/dist/server.py"] From 953a024ff1fc308a6d71cee7acd4a6afb4e07d90 Mon Sep 17 00:00:00 2001 From: GM Date: Sun, 22 Jan 2023 21:44:34 +0000 Subject: [PATCH 23/27] just for commit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 3a96222..c1ce59e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ COPY --from=pybuilder /code /dist # copy the frontend static files from the builder COPY --from=builder ./dist /dist/ WORKDIR /dist +# create dir for save RUN mkdir save EXPOSE 5001 From c3a53b7ace115237f834b91c5925469c17b0dd10 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 22 Jan 2023 23:03:26 +0000 Subject: [PATCH 24/27] add more importance to a dead role --- backend/bang/game.py | 11 ++ .../src/components/DeadRoleNotification.vue | 153 ++++++++++++++++++ frontend/src/components/Lobby.vue | 32 ++-- 3 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 frontend/src/components/DeadRoleNotification.vue diff --git a/backend/bang/game.py b/backend/bang/game.py index cef7972..4340e80 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -747,6 +747,17 @@ class Game: G.sio.emit('chat_message', room=self.name, data=f'_died|{player.name}') if self.started: G.sio.emit('chat_message', room=self.name, data=f'_died_role|{player.name}|{player.role.name}') + if not isinstance(player.role, roles.Sheriff) and not self.initial_players == 3: + G.sio.emit('notify_dead_role', room=self.name, data={ + 'name': player.name, + 'lives': 0, + 'max_lives': player.max_lives, + 'is_ghost': player.is_ghost, + 'is_bot': player.is_bot, + 'icon': '🤠', + 'avatar': player.avatar, + 'role': player.role.__dict__, + }) for p in self.players: if not p.is_bot: p.notify_self() diff --git a/frontend/src/components/DeadRoleNotification.vue b/frontend/src/components/DeadRoleNotification.vue new file mode 100644 index 0000000..b03e79f --- /dev/null +++ b/frontend/src/components/DeadRoleNotification.vue @@ -0,0 +1,153 @@ + + + + + + diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index fc72247..d74d60a 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -93,6 +93,9 @@ + + + @@ -107,6 +110,7 @@ import Deck from './Deck.vue' import TinyHand from './TinyHand.vue' import FullScreenInput from './FullScreenInput.vue' import Status from './Status.vue' +import DeadRoleNotification from './DeadRoleNotification.vue' export default { name: 'Lobby', @@ -119,7 +123,8 @@ export default { TinyHand, PrettyCheck, FullScreenInput, - Status + Status, + DeadRoleNotification }, data: () => ({ username: '', @@ -147,6 +152,7 @@ export default { displayAdminStatus: false, is_replay: false, turn: -1, + deadRoleData: null, }), sockets: { room(data) { @@ -174,6 +180,12 @@ export default { } }) }, + notify_dead_role(data) { + this.deadRoleData = data + setTimeout(() => { + this.deadRoleData = null + }, 4000); + }, debug(data) { this.debug_mode = data; }, @@ -191,28 +203,10 @@ export default { this.$router.push('/') } this.username = username - // this.$socket.emit('get_cards', 'dodge_city') }, mount_status() { this.displayAdminStatus = true }, - // cards_info(data) { - // data = JSON.parse(data) - // let bigthing = {} - // let bigthing_eng = {} - // data.forEach(x => { - // bigthing[x.name] = { - // name:x.name, - // desc:x.desc, - // } - // bigthing_eng[x.name] = { - // name:x.name, - // desc:x.desc_eng, - // } - // }) - // console.log(JSON.stringify(bigthing)) - // console.log(JSON.stringify(bigthing_eng)) - // }, change_username() { this.hasToSetUsername = true }, From c888632fa7e668573a567848edd4be91ab245765 Mon Sep 17 00:00:00 2001 From: GM Date: Mon, 23 Jan 2023 08:59:48 +0000 Subject: [PATCH 25/27] create the folder if it doesn't exist --- .gitignore | 2 +- backend/server.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d81a0a5..47f5590 100644 --- a/.gitignore +++ b/.gitignore @@ -141,4 +141,4 @@ frontend/package-lock.json bang-workspace.code-workspace .vscode/ -backend/games.pickle +backend/save/* \ No newline at end of file diff --git a/backend/server.py b/backend/server.py index d726ab2..c1f4695 100644 --- a/backend/server.py +++ b/backend/server.py @@ -813,6 +813,8 @@ def save_games(): while True: sio.sleep(2) if not save_lock: + if not os.path.exists("save"): + os.mkdir("save") with open('./save/games.pickle', 'wb') as f: pickle.dump([g for g in games if g.started and not g.is_replay and not g.is_hidden], f) From 218e348a53d48ca9a6df33173cdcc4e908433c08 Mon Sep 17 00:00:00 2001 From: GM Date: Mon, 23 Jan 2023 09:08:09 +0000 Subject: [PATCH 26/27] view the expansion valley_of_shadows only once --- backend/bang/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index 4340e80..f12705b 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -221,7 +221,7 @@ class Game: self.notify_room() def feature_flags(self): - if 'the_valley_of_shadows' not in self.expansions: + if 'the_valley_of_shadows' not in self.expansions and 'the_valley_of_shadows' not in self.available_expansions : self.available_expansions.append('the_valley_of_shadows') self.notify_room() From 10ea588a16044c0b7182677568233e39090cfe72 Mon Sep 17 00:00:00 2001 From: GM Date: Mon, 23 Jan 2023 10:23:03 +0000 Subject: [PATCH 27/27] fix status page --- backend/server.py | 12 ++++++++---- frontend/src/components/Lobby.vue | 2 +- frontend/src/components/Status.vue | 5 +++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/backend/server.py b/backend/server.py index c1f4695..440ef51 100644 --- a/backend/server.py +++ b/backend/server.py @@ -299,7 +299,8 @@ Sockets for the status page @sio.event @bang_handler def get_all_rooms(sid, deploy_key): - if ('DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): + ses = sio.get_session(sid) + if ('DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): sio.emit('all_rooms', room=sid, data=[{ 'name': g.name, 'hidden': g.is_hidden, @@ -316,14 +317,16 @@ def get_all_rooms(sid, deploy_key): @sio.event @bang_handler def kick(sid, data): - if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): + ses = sio.get_session(sid) + if ('DEPLOY_KEY' in os.environ and 'key' in data and data['key'] == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): sio.emit('kicked', room=data['sid']) @sio.event @bang_handler def reset(sid, data): global games - if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): + ses = sio.get_session(sid) + if ('DEPLOY_KEY' in os.environ and 'key' in data and data['key'] == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): for g in games: sio.emit('kicked', room=g.name) games = [] @@ -331,7 +334,8 @@ def reset(sid, data): @sio.event @bang_handler def hide_toogle(sid, data): - if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin(): + ses = sio.get_session(sid) + if ('DEPLOY_KEY' in os.environ and 'key' in data and data['key'] == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): game = [g for g in games if g.name==data['room']] if len(games) > 0: game[0].is_hidden = not game[0].is_hidden diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index d74d60a..a8499e4 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -90,7 +90,7 @@
    - +
    diff --git a/frontend/src/components/Status.vue b/frontend/src/components/Status.vue index f598b57..cddb121 100644 --- a/frontend/src/components/Status.vue +++ b/frontend/src/components/Status.vue @@ -38,7 +38,7 @@ export default { props: { onpage: { type: Boolean, - default: false + default: true } }, data:()=>({ @@ -63,7 +63,8 @@ export default { }, reset(){ if (confirm('ARE YOU SURE? KICK EVERYONE AND RESET LOBBIES?')) - this.$socket.emit('reset', this.deploy_key) + this.$socket.emit('reset', {'key':this.deploy_key}) + this.refresh(); }, hide(room_name){ this.$socket.emit('hide_toogle', {'key':this.deploy_key, 'room':room_name})