From 7f9e0bd96469d73c9f3a2c280ae609a8fd6fefe1 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 7 Oct 2021 16:53:53 +0300 Subject: [PATCH] work on replays Co-authored-by: GM --- backend/__init__.py | 51 ++++++----------------------------- backend/bang/game.py | 47 +++++++++++++++++++++++++++++++- backend/bang/players.py | 28 ++++++++++++++++--- backend/tests/dummy_socket.py | 5 ++++ 4 files changed, 84 insertions(+), 47 deletions(-) diff --git a/backend/__init__.py b/backend/__init__.py index 8771f72..0c78366 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -1,7 +1,6 @@ import os import json import random -from tests.dummy_socket import DummySocket from typing import List import eventlet import socketio @@ -335,7 +334,7 @@ def chat_message(sid, msg, pl=None): bot.bot_spin() return if '/report' in msg and not ses.game.is_replay: - data = "\n".join(ses.game.rpc_log).strip() + data = "\n".join(ses.game.rpc_log[:-1]).strip() response = requests.post("http://hastebin.com/documents", data) key = json.loads(response.text).get('key') webhook = DiscordWebhook(url=os.environ['DISCORD_WEBHOOK'], content=f'New bug report, replay at https://hastebin.com/{key}') @@ -348,47 +347,13 @@ def chat_message(sid, msg, pl=None): response = requests.get(f"http://hastebin.com/raw/{replay_id}") log = response.text.splitlines() ses.game.spectators.append(ses) - ses.game.players = [] - ses.game.is_replay = True - for i in range(len(log)): - print('replay:', i, 'of', len(log)) - cmd = log[i].split(';') - if cmd[1] == 'players': - ses.game.expansions = json.loads(cmd[4].replace("'",'"')) - pnames = json.loads(cmd[3].replace("'",'"')) - for p in pnames: - ses.game.add_player(Player(p, p, DummySocket(), bot=False)) - continue - if cmd[1] == 'start_game': - ses.game.start_game(int(cmd[2])) - continue - player = [p for p in ses.game.players if p.name == cmd[0]][0] - if cmd[1] == 'set_character': - player.set_character(cmd[2]) - if cmd[1] == 'draw': - player.draw(cmd[2]) - if cmd[1] == 'pick': - player.pick() - if cmd[1] == 'end_turn': - player.end_turn() - if cmd[1] == 'play_card': - data = json.loads(cmd[2]) - player.play_card(data['index'], data['against'], data['with']) - if cmd[1] == 'respond': - player.respond(int(cmd[2])) - if cmd[1] == 'choose': - player.choose(int(cmd[2])) - if cmd[1] == 'scrap': - player.scrap(int(cmd[2])) - if cmd[1] == 'special': - player.special(json.loads(cmd[2])) - if cmd[1] == 'gold_rush_discard': - player.gold_rush_discard() - if cmd[1] == 'buy_gold_rush_card': - player.buy_gold_rush_card(int(cmd[2])) - if cmd[1] == 'chat_message': - chat_message(None, cmd[2], player) - eventlet.sleep(1) + ses.game.replay(log) + return + if '/replayspeed' in msg: + _cmd = msg.split() + if len(_cmd) == 2: + ses.game.replay_speed = float(cmd[1]) + return if '/startwithseed' in msg and not ses.game.started: if len(msg.split()) > 1: ses.game.start_game(int(msg.split()[1])) diff --git a/backend/bang/game.py b/backend/bang/game.py index bebba85..00577da 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -13,7 +13,6 @@ 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 - class Game: def __init__(self, name, sio:socketio): super().__init__() @@ -74,6 +73,52 @@ class Game: eventlet.sleep(0.5) self.notify_room() + def replay(self, log): + from tests.dummy_socket import DummySocket + self.players = [] + self.is_hidden = True + self.is_replay = True + self.replay_speed = 1 + for i in range(len(log)): + print('replay:', i, 'of', len(log)) + cmd = log[i].split(';') + if cmd[1] == 'players': + 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)) + continue + if cmd[1] == 'start_game': + self.start_game(int(cmd[2])) + continue + player = [p for p in self.players if p.name == cmd[0]][0] + if cmd[1] == 'set_character': + player.set_character(cmd[2]) + if cmd[1] == 'draw': + player.draw(cmd[2]) + if cmd[1] == 'pick': + player.pick() + if cmd[1] == 'end_turn': + player.end_turn() + if cmd[1] == 'play_card': + data = json.loads(cmd[2]) + player.play_card(data['index'], data['against'], data['with']) + if cmd[1] == 'respond': + player.respond(int(cmd[2])) + if cmd[1] == 'choose': + player.choose(int(cmd[2])) + if cmd[1] == 'scrap': + player.scrap(int(cmd[2])) + if cmd[1] == 'special': + player.special(json.loads(cmd[2])) + if cmd[1] == 'gold_rush_discard': + player.gold_rush_discard() + if cmd[1] == 'buy_gold_rush_card': + player.buy_gold_rush_card(int(cmd[2])) + # if cmd[1] == 'chat_message': + # chat_message(None, cmd[2], player) + eventlet.sleep(self.replay_speed) + def notify_room(self, sid=None): if len([p for p in self.players if p.character == None]) != 0 or sid: self.sio.emit('room', room=self.name if not sid else sid, data={ diff --git a/backend/bang/players.py b/backend/bang/players.py index 943aa6e..a80c142 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -135,7 +135,9 @@ class Player: self.sio.emit('characters', room=self.sid, data=json.dumps( available, default=lambda o: o.__dict__)) else: - self.set_character(available[randrange(0, len(available))].name) + char_name = available[randrange(0, len(available))].name + self.game.rpc_log.append(f'{self.name};set_character;{char_name}') + self.set_character(char_name) def notify_card(self, player, card, message=''): try: @@ -233,8 +235,10 @@ class Player: else: return if self.pending_action == PendingAction.PICK: + self.game.rpc_log.append(f'{self.name};pick;') self.pick() elif self.pending_action == PendingAction.DRAW: + 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)] @@ -253,6 +257,7 @@ class Player: if self.gold_nuggets > 0 and any([c.number <= self.gold_nuggets for c in self.game.deck.shop_cards]): for i in range(len(self.game.deck.shop_cards)): if self.game.deck.shop_cards[i].number <= self.gold_nuggets: + self.game.rpc_log.append(f'{self.name};buy_gold_rush_card;{i}') self.buy_gold_rush_card(i) return if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice): @@ -305,26 +310,33 @@ class Player: if maxcards == self.lives and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Cinturone)]) > 0: maxcards = 8 if len(self.hand) > maxcards: + self.game.rpc_log.append(f'{self.name};scrap;{0}') self.scrap(0) else: + self.game.rpc_log.append(f'{self.name};end_turn') self.end_turn() elif self.pending_action == PendingAction.RESPOND: did_respond = False for i in range(len(self.hand)): if self.hand[i].can_be_used_now and (self.hand[i].name in self.expected_response or self.character.check(self.game, chd.ElenaFuente)): + self.game.rpc_log.append(f'{self.name};respond;{i}') self.respond(i) did_respond = True break for i in range(len(self.equipment)): if not self.game.check_event(ce.Lazo) and self.equipment[i].name in self.expected_response: + self.game.rpc_log.append(f'{self.name};respond;{len(self.hand)+i}') self.respond(len(self.hand)+i) did_respond = True break if not did_respond: + self.game.rpc_log.append(f'{self.name};respond;{-1}') self.respond(-1) elif self.pending_action == PendingAction.CHOOSE: if not self.target_p: - self.choose(randrange(0, len(self.available_cards))) + card_index = randrange(0, len(self.available_cards)) + self.game.rpc_log.append(f'{self.name};choose;{card_index}') + self.choose(card_index) else: target = self.game.get_player_named(self.target_p) if len(target.hand)+len(target.equipment) == 0: @@ -332,8 +344,11 @@ class Player: self.notify_self() else: try: - self.choose(randrange(0, len(target.hand)+len(target.equipment))) + card_index = randrange(0, len(target.hand)+len(target.equipment)) + self.game.rpc_log.append(f'{self.name};choose;{card_index}') + self.choose(card_index) except: + self.game.rpc_log.append(f'{self.name};choose;{0}') self.choose(0) @@ -577,6 +592,13 @@ class Player: return playable_cards def play_card(self, hand_index: int, against=None, _with=None): + if self.is_bot: + data = { + "index": hand_index, + "against": against, + "with": _with + } + self.game.rpc_log.append(f'{self.name};play_card;{json.dumps(data)}') print(self.name, 'wants to play card ', hand_index, ' against:', against, ' with:', _with) if not self.is_my_turn or self.pending_action != PendingAction.PLAY or self.game.is_handling_death: print('but cannot') diff --git a/backend/tests/dummy_socket.py b/backend/tests/dummy_socket.py index 6f4711c..554af02 100644 --- a/backend/tests/dummy_socket.py +++ b/backend/tests/dummy_socket.py @@ -1,6 +1,11 @@ class DummySocket(): + def __init__(self, sio=None): + self.true_sio = sio + def emit(self, event, data=None, to=None, room=None, skip_sid=None, namespace=None, callback=None, **kwargs): # print(f'event: {event}, data: {data}, to: {to}, room: {room}') + if self.true_sio and event == 'chat_message': + self.true_sio.emit(event, data, to, room, skip_sid, namespace, callback, **kwargs) return True is_fake = True \ No newline at end of file