def _get_all_data(): return { '_ALL_MATCHES': MatchDB.dump_table(_ALL_MATCHES), '_USER_2_MATCH_ID': MatchDB.dump_table(_USER_2_MATCH_ID), '_PRIVATE_PENDING_MATCH_IDS': MatchDB.dump_table(_PRIVATE_PENDING_MATCH_IDS), '_PUBLIC_PENDING_MATCH_ID': MatchDB.get_queue(_PUBLIC_PENDING_MATCH_IDS, None) }
def lock_and_get_chessboard(self): redis_lock, acquired = MatchDB.lock('chessboard', self.match_id, settings.GAME_TTL) if not acquired: return redis_lock, None else: return redis_lock, self.chessboard
def _create_match(player_uid1, join_token): match = Match(player_uid1, join_token) success = MatchDB.set(_USER_2_MATCH_ID, player_uid1, match.match_id, xx=True) if not success: raise ValueError( 'player {} should register before creating a match'.format( player_uid1)) if join_token: success = MatchDB.set(_PRIVATE_PENDING_MATCH_IDS, join_token, match.match_id, nx=True) if not success: raise exceptions.InvalidMatchState( 'join_token {} is already in use by at least 2 players.'. format(join_token)) else: MatchDB.enqueue(_PUBLIC_PENDING_MATCH_IDS, None, match.match_id) success = MatchDB.set(_ALL_MATCHES, match.match_id, match.to_dict(), nx=True) if not success: raise exceptions.InvalidMatchState( 'Match {} is already created'.format(match.match_id)) chessboard = Chessboard() MatchDB.set(_CHESSBOARD, match.chessboard_id, chessboard.to_dict()) return match
def join_match(player_uid, join_token): try: _register_player(player_uid, bool(join_token)) except exceptions.AlreadyInMatchException: match_id = MatchDB.get(_USER_2_MATCH_ID, player_uid) match_dict = MatchDB.get(_ALL_MATCHES, match_id) if match_dict: match = Match.from_dict(match_dict) if match.join_token == join_token: return match leave_match(player_uid) _register_player(player_uid, bool(join_token)) try: if join_token: blocking_timeout = min(settings.GAME_TTL // 2, 5) match_room_door, can_use_door = MatchDB.lock( 'match_room_door', join_token, blocking_timeout=blocking_timeout) if not can_use_door: raise exceptions.CannotAcquireMatchDoor( 'the door {} is too crowded.'.format(join_token)) population = MatchDB.enter_private_match(join_token) if population == 1: # going to create a match match_id = None elif population == 2: # going to join an existing match match_id = MatchDB.takeaway(_PRIVATE_PENDING_MATCH_IDS, join_token) elif population > 2: # join_token collision. try another join_token raise exceptions.InvalidMatchState( 'join_token {} is already in use by at least 2 players.'. format(join_token)) else: match_id = MatchDB.dequeue(_PUBLIC_PENDING_MATCH_IDS, None, False) if match_id is None: match = _create_match(player_uid, join_token) else: match = Match.from_dict(MatchDB.get(_ALL_MATCHES, match_id)) match.set_player2(player_uid) MatchDB.set(_USER_2_MATCH_ID, player_uid, match.match_id) match.save() match.send_message_from(player_uid, msg_meta.MSG_TYPE_CONTROL, msg_meta.MSG_CONST_JOIN) return match finally: if join_token: match_room_door.release()
def handle_reply_undo_request(message): msg_data = json.loads(message['msg_data']) match = match_driver.get_match(current_user.user_id) if not msg_data or not msg_data.get('approved'): msg_data = {'approved': False} match.send_message_from(current_user.user_id, MSG_TYPE_REPLYUNDOREQ, msg_data) return msg_data try: lock, chessboard = match.lock_and_get_chessboard() if not chessboard: match.send_message_from(current_user.user_id, MSG_TYPE_REPLYUNDOREQ, msg_data) return msg_data step = chessboard.undo() if not step: match.send_message_from(current_user.user_id, MSG_TYPE_REPLYUNDOREQ, msg_data) return msg_data match.chessboard = chessboard step['undone_color'] = Chessman.id2color(step['chess_id']).value kill_chess_id = step['kill_chess_id'] if kill_chess_id is not None: killed_color = Chessman.id2color(kill_chess_id) step['killed_color'] = killed_color.value step['killed_char'] = Chessman.role2char( Chessman.id2role(kill_chess_id), color=killed_color) step['killed_pic'] = Chessman.role2pic( Chessman.id2role(kill_chess_id), color=killed_color) msg_data = {'approved': True, 'step': step} match.send_message_from(current_user.user_id, MSG_TYPE_REPLYUNDOREQ, msg_data) return msg_data finally: MatchDB.delete( 'undoreq', "{}-{}".format(match.another_player_uid, match.match_id)) lock.release()
def handle_draw_request(message): match = match_driver.get_match(current_user.user_id) if match.is_over: return {'result': False} # If your draw request within the past 60s was not approved, by the ohter # player, then you can't request again. can_request = MatchDB.set('drawreq', "{}-{}".format(current_user.user_id, match.match_id), 'requesting', nx=True, ex=60) if not can_request: return {'result': False} msg_type = message['msg_type'] msg_data = message['msg_data'] match.send_message_from(current_user.user_id, msg_type, msg_data) return {'result': True}
def _register_player(user_id, is_public_game): success = MatchDB.set(_USER_2_MATCH_ID, user_id, is_public_game, nx=True) if not success: raise exceptions.AlreadyInMatchException()
def set_chessboard(chessboard_id, chessboard): return MatchDB.set(_CHESSBOARD, chessboard_id, chessboard.to_dict())
def get_chessboard(chessboard_id): return Chessboard.from_dict(MatchDB.get(_CHESSBOARD, chessboard_id))
def get_match(player_uid): match_id = MatchDB.get(_USER_2_MATCH_ID, player_uid) if not match_id: raise exceptions.NoMatchFoundException() return Match.from_dict(MatchDB.get(_ALL_MATCHES, match_id))
def saveMatch(match): MatchDB.set(_ALL_MATCHES, match.match_id, match.to_dict())
def leave_match(player_uid): blocking_timeout = min(settings.GAME_TTL // 2, 5) match_id = MatchDB.takeaway(_USER_2_MATCH_ID, player_uid) if not match_id: raise exceptions.InvalidMatchState( "Can't find the match_id for user {}.".format(player_uid)) try: join_token = None match = Match.from_dict(MatchDB.get(_ALL_MATCHES, match_id)) if not match: raise exceptions.InvalidMatchState( "Can't find the match {} to leave.".format(match_id)) join_token = match.join_token if join_token: match_room_door, can_use_door = MatchDB.lock( 'match_room_door', join_token, blocking_timeout=blocking_timeout) if not can_use_door: raise exceptions.CannotAcquireMatchDoor( 'the door {} is too crowded.'.format(join_token)) match.remove_player(player_uid) MatchDB.delete(_USER_2_MATCH_ID, player_uid) if match.active_players_cnt == 1: match.send_message_from(player_uid, msg_meta.MSG_TYPE_CONTROL, msg_meta.MSG_CONST_LEFT) MatchDB.set(_ALL_MATCHES, match.match_id, match.to_dict()) return # the last one leaving the match should clean up the mess match = Match.from_dict(MatchDB.takeaway(_ALL_MATCHES, match_id)) if match.join_token: MatchDB.delete(_PRIVATE_PENDING_MATCH_IDS, match.join_token) else: MatchDB.force_remove_from_queue(_PUBLIC_PENDING_MATCH_IDS, None, match_id) MatchDB.delete(_CHESSBOARD, match.chessboard_id) finally: if join_token: match_room_door.release()
def receive_message_to(self, my_uid): channel_name = self._channel_to(my_uid) message = MatchDB.dequeue('match_channel', channel_name, False) return message or {'msg_type': msg_meta.MSG_TYPE_NOP, 'msg_data': None}
def send_message_from(self, my_uid, msg_type, msg_data): for other_uid in self.player_uids: if other_uid != my_uid: channel_name = self._channel_to(other_uid) message = {'msg_type': msg_type, 'msg_data': msg_data} MatchDB.enqueue('match_channel', channel_name, message)
def get_message_queue_length(self, my_uid): channel_name = self._channel_to(my_uid) return MatchDB.queue_length('match_channel', channel_name)