From a48c4ef2b3a79aca2edaea240a7ed973ea4ab094 Mon Sep 17 00:00:00 2001 From: Neel Somani Date: Mon, 27 Apr 2020 18:07:10 -0700 Subject: [PATCH] Implement PING/PONG for WebSockets to keep Heroku alive (#15) --- backend.py | 17 ++++++++++++++--- constants.py | 1 + src/components/Constants.js | 2 ++ src/views/Game.js | 16 +++++++++++++++- test_backend.py | 11 ++++++++++- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/backend.py b/backend.py index 031d1c4..2f1bc5e 100644 --- a/backend.py +++ b/backend.py @@ -174,6 +174,8 @@ def register_with_uuid(self, player_uuid): """ Send the client the info for the specified `player_uuid`. The `player_uuid` must already be present in `self.players`. + + Send the player names if they have updated. """ player_n = self.users[player_uuid].player_n payload = { @@ -191,7 +193,8 @@ def register_with_uuid(self, player_uuid): self.logger.info('Sent registration to user {}'.format(player_uuid)) username = self.users[player_uuid].username if self.player_names.get(player_n) != username: - self.player_names[player_n] = username + if player_n != VISITOR_PLAYER_ID: + self.player_names[player_n] = username self._send_player_names() if self.current_players == self.n_players: @@ -224,7 +227,8 @@ def _send(self, client, data): self.logger.info( 'Player {} is disconnected from game {}' .format(player_uuid, self.uuid)) - self.player_names[u.player_n] = u.username + if u.player_n != VISITOR_PLAYER_ID: + self.player_names[u.player_n] = u.username self._send_player_names() def _send_all(self, message): @@ -250,12 +254,19 @@ def _fill_bots(self, message): util.BotClient(), 'Bot {}'.format(self.current_players + i)) + def _ping_pong(self, payload): + user = self.users[payload['key']] + gevent.spawn(self._send, user.socket, { + 'action': PING_PONG + }) + def handle_message(self, message): action_map = { CLAIM: self._claim, MOVE: self._move, SWITCH_TEAM: self._switch_team, - START_GAME: self._fill_bots + START_GAME: self._fill_bots, + PING_PONG: self._ping_pong } fn = action_map.get(message['action']) if fn is None: diff --git a/constants.py b/constants.py index 5094f99..901e155 100644 --- a/constants.py +++ b/constants.py @@ -10,3 +10,4 @@ SWITCH_TEAM = 'switch_team' START_GAME = 'start_game' PLAYER_NAMES = 'player_names' +PING_PONG = 'ping_pong' diff --git a/src/components/Constants.js b/src/components/Constants.js index ba99066..77da41c 100644 --- a/src/components/Constants.js +++ b/src/components/Constants.js @@ -35,3 +35,5 @@ export const UNCLAIMED = 'unclaimed'; export const PLAYER_UUID = 'player_uuid'; export const PLAYER_NAME = 'player_name'; + +export const PING_PONG_INTERVAL_MS = 5000 diff --git a/src/views/Game.js b/src/views/Game.js index bd28b7e..0b77c42 100644 --- a/src/views/Game.js +++ b/src/views/Game.js @@ -14,7 +14,8 @@ import { UNCLAIMED, SET_NAME_MAP, PLAYER_UUID, - PLAYER_NAME + PLAYER_NAME, + PING_PONG_INTERVAL_MS } from '../components/Constants'; import './Game.css'; @@ -40,6 +41,17 @@ class Game extends Component { }; const audioUrl = process.env.PUBLIC_URL + '/bell.mp3'; this.bell = new Audio(audioUrl); + setInterval(this.pingPong.bind(this), PING_PONG_INTERVAL_MS) + } + + pingPong() { + this.sendMessage({ + 'action': 'ping_pong', + 'game_uuid': this.state.gameUuid, + 'payload': { + 'key': this.state.uuid + } + }) } defaultNames() { @@ -246,6 +258,8 @@ class Game extends Component { case 'player_names': this.playerNames(data.payload) break; + case 'ping_pong': + break; default: throw new Error('Unhandled action: ' + data.action); } diff --git a/test_backend.py b/test_backend.py index 72a71f0..0973ff5 100644 --- a/test_backend.py +++ b/test_backend.py @@ -104,11 +104,20 @@ def test_registration(api): assert len(c.messages) == 2 msg = _action_from_messages(c.messages, REGISTER) assert msg['player_n'] != VISITOR_PLAYER_ID - assert 'player_uuid' in msg + player_uuid = msg['player_uuid'] + game_uuid = msg['game_uuid'] assert msg['time_limit'] == TIME_LIMIT assert msg['n_players'] == N_PLAYERS msg = _action_from_messages(c.messages, PLAYER_NAMES) assert msg['names']['0'] == MOCK_NAME + api.handle_message({ + 'action': PING_PONG, + 'game_uuid': game_uuid, + 'payload': { + 'key': player_uuid + } + }) + assert c.messages[-1]['action'] == PING_PONG def _action_from_messages(messages, action):