def set(cls, service_name): try: ServiceStatus.set_status(service_name, request.params) return {'ok': True} except InvalidStatus: response.status_int = 400 msg = 'Missing or invalid service status arguments. Must be running, discardRequests or description' return {'ok': False, 'msg': msg}
def set(cls, service_name): try: ServiceStatus.set_status(service_name, request.params) return {'ok': True} except InvalidStatus: response.status_int = 400 msg = 'Missing or invalid service status arguments. Must be running, discardRequests or description' return {'ok': False, 'msg': msg}
def read_list(cls): try: return {'ok': True, 'data': { 'services': ServiceStatus.get_status_list(), 'pollInterval': ServiceStatus.get_poll_interval() }} except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing service name'}
def set_poll_interval(cls): try: poll_interval = float(request.params['value']) if poll_interval <= 0: raise ValueError ServiceStatus.set_poll_interval(poll_interval) return {'ok': True} except (KeyError, ValueError): response.status_int = 400 return {'ok': False, 'msg': 'Polling interval must be a positive value'}
def read_list(cls): try: return { 'ok': True, 'data': { 'services': ServiceStatus.get_status_list(), 'pollInterval': ServiceStatus.get_poll_interval() } } except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing service name'}
def set_poll_interval(cls): try: poll_interval = float(request.params['value']) if poll_interval <= 0: raise ValueError ServiceStatus.set_poll_interval(poll_interval) return {'ok': True} except (KeyError, ValueError): response.status_int = 400 return { 'ok': False, 'msg': 'Polling interval must be a positive value' }
class ProfilesController(BaseController): """ ProfilesController consists of all the Profiles methods """ profiles_service = ServiceStatus.check_status_decorator('profiles') ## ## FRONT CONTROLLER METHODS ## @classmethod @profiles_service @jsonify def user(cls): user = get_current_user() user_profile = {'username': user.username, 'displayname': user.username, 'age': user.age, 'language': user.language, 'country': user.country, 'avatar': user.avatar, 'guest': user.guest} return {'ok': True, 'data': user_profile}
class GamesController(BaseController): """ Controller class for the 'play' branch of the URL tree. """ gamesession_service = ServiceStatus.check_status_decorator('gameSessions') @classmethod @gamesession_service @jsonify def create_session(cls, slug, mode=None): """ Returns application settings for local. """ game = get_game_by_slug(slug) if not game: response.status_int = 404 return {'ok': False, 'msg': 'Game does not exist: %s' % slug} if 'canvas' == mode: prefix = 'play/%s/' % slug else: prefix = '' mapping_table = 'mapping_table.json' if game: mapping_table = str(game.mapping_table) user = get_current_user() game_session_list = GameSessionList.get_instance() if (asbool(request.params.get('closeExistingSessions', False))): game_session_list.remove_game_sessions(user, game) game_session = game_session_list.create_session(user, game) # Reset API's (so YAML files are reloaded on a page refresh) StoreList.reset() DataShareList.reset() GameNotificationKeysList.reset() return { 'ok': True, 'mappingTable': { 'mappingTableURL': prefix + mapping_table, 'mappingTablePrefix': prefix + 'staticmax/', 'assetPrefix': 'missing/' }, 'gameSessionId': game_session.gamesession_id } @classmethod @gamesession_service @jsonify def destroy_session(cls): """ Ends a session started with create_session. """ try: game_session_id = request.params['gameSessionId'] user = get_current_user() game_session_list = GameSessionList.get_instance() session = game_session_list.get_session(game_session_id) if session is not None: if session.user.username == user.username: game_session_list.remove_session(game_session_id) return {'ok': True} else: response.status_int = 400 return { 'ok': False, 'msg': "Attempted to end a session that is not owned by you" } else: response.status_int = 400 return { 'ok': False, 'msg': 'No session with ID "%s" exists' % game_session_id } except TypeError, e: response.status_int = 400 return {'ok': False, 'msg': 'Something is missing: %s' % str(e)} except KeyError, e: response.status_int = 400 return {'ok': False, 'msg': 'Something is missing: %s' % str(e)}
class GameprofileController(BaseController): """ GameprofileController consists of all the GameProfile methods """ game_session_list = GameSessionList.get_instance() game_profile_service = ServiceStatus.check_status_decorator('gameProfile') max_size = int(config.get('gameprofile.max_size', 1024)) max_list_length = int(config.get('gameprofile.max_list_length', 64)) @classmethod def __get_profile(cls, params): """ Get the user and game for this game session """ try: session = cls.game_session_list.get_session(params['gameSessionId']) return GameProfile(session.user, session.game) except (KeyError, TypeError): return None @classmethod @game_profile_service @jsonify def read(cls): params = request.params try: usernames = loads(params['usernames']) except (KeyError, TypeError): response.status_int = 400 return {'ok': False, 'msg': 'Missing username information'} except ValueError: response.status_int = 400 return {'ok': False, 'msg': 'Badly formated username list'} if not isinstance(usernames, list): response.status_int = 400 return {'ok': False, 'msg': '\'usernames\' must be a list'} max_list_length = cls.max_list_length if len(usernames) > max_list_length: response.status_int = 413 return {'ok': False, 'msg': 'Cannot request game profiles ' \ 'for more than %d users at once' % max_list_length} profile = cls.__get_profile(params) if profile is None: response.status_int = 400 return {'ok': False, 'msg': 'No session with that ID exists'} return {'ok': True, 'data': profile.get(usernames)} @classmethod @game_profile_service @secure_post def set(cls, params=None): try: value = str(params['value']) except (KeyError, TypeError): response.status_int = 400 return {'ok': False, 'msg': 'No profile value provided to set'} except ValueError: response.status_int = 400 return {'ok': False, 'msg': '\'value\' should not contain non-ascii characters'} value_length = len(value) if value_length > cls.max_size: response.status_int = 413 return {'ok': False, 'msg': 'Value length should not exceed %d' % cls.max_size} profile = cls.__get_profile(params) if profile is None: response.status_int = 400 return {'ok': False, 'msg': 'No session with that ID exists'} profile.set(value) return {'ok': True} @classmethod @game_profile_service @secure_post def remove(cls, params=None): profile = cls.__get_profile(params) if profile is None: response.status_int = 400 return {'ok': False, 'msg': 'No session with that ID exists'} profile.remove() return {'ok': True} # testing only @classmethod @game_profile_service @jsonify def remove_all(cls, slug): game = get_game_by_slug(slug) if game is None: response.status_int = 400 return {'ok': False, 'msg': 'No game with that slug exists'} GameProfile.remove_all(game) return {'ok': True}
class StoreController(BaseController): """ StoreController consists of all the store methods """ store_service = ServiceStatus.check_status_decorator('store') game_session_list = GameSessionList.get_instance() @classmethod @store_service @jsonify def get_currency_meta(cls): return {'ok': True, 'data': get_currency_meta()} @classmethod @store_service @jsonify def read_meta(cls, slug): game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with slug %s' % slug} try: store = StoreList.get(game) return { 'ok': True, 'data': { 'items': store.read_meta(), 'resources': store.read_resources() } } except StoreUnsupported: return {'ok': True, 'data': {'items': {}, 'resources': {}}} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @jsonify def read_user_items(cls, slug): user = get_current_user() game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with slug %s' % slug} try: store = StoreList.get(game) store_user = store.get_store_user(user) return {'ok': True, 'data': {'userItems': store_user.get_items()}} except StoreUnsupported: return {'ok': True, 'data': {'userItems': {}}} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @secure_post def consume_user_items(cls, params=None): session = cls._get_gamesession(params) try: def get_param(param): value = params[param] if value is None: raise KeyError(param) return value consume_item = get_param('key') consume_amount = get_param('consume') token = get_param('token') gamesession_id = get_param('gameSessionId') except KeyError as e: response.status_int = 400 return {'ok': False, 'msg': 'Missing parameter %s' % str(e)} try: game = session.game user = session.user store = StoreList.get(game) transactions = UserTransactionsList.get(user) # check if the transaction has already been attempted consume_transaction = transactions.get_consume_transaction( gamesession_id, token) new_consume_transaction = ConsumeTransaction( user, game, consume_item, consume_amount, gamesession_id, token) if consume_transaction is None: consume_transaction = new_consume_transaction elif not consume_transaction.check_match(new_consume_transaction): response.status_int = 400 return {'ok': False, 'msg': 'Reused session token'} if not consume_transaction.consumed: consume_transaction.consume() store_user = store.get_store_user(user) return { 'ok': True, 'data': { 'consumed': consume_transaction.consumed, 'userItems': store_user.get_items() } } except StoreUnsupported: return { 'ok': True, 'data': { 'compareAndSet': False, 'userItems': {} } } except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @postonly @jsonify def remove_all(cls, slug): user = get_current_user() game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with slug %s' % slug} try: store = StoreList.get(game) store.get_store_user(user).remove_items() return {'ok': True} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @postonly @jsonify def checkout_transaction(cls): user = get_current_user() try: game_slug = request.POST['gameSlug'] transaction_items_json = request.POST['basket'] except KeyError as e: response.status_int = 400 return {'ok': False, 'msg': 'Missing parameter %s' % str(e)} try: transaction_items = _json_decoder.decode(transaction_items_json) except JSONDecodeError as e: response.status_int = 400 return { 'ok': False, 'msg': 'Basket parameter JSON error: %s' % str(e) } if not isinstance(transaction_items, dict): response.status_int = 400 return { 'ok': False, 'msg': 'Basket parameter JSON must be a dictionary' } game = get_game_by_slug(game_slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with slug %s' % game_slug} try: transaction = Transaction(user, game, transaction_items) return {'ok': True, 'data': {'transactionId': transaction.id}} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @postonly @jsonify def pay_transaction(cls, transaction_id): user = get_current_user() try: user_transactions = UserTransactionsList.get(user) transaction = user_transactions.get_transaction(transaction_id) transaction.pay() return {'ok': True, 'data': transaction.status()} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @store_service @jsonify def read_transaction_status(cls, transaction_id): user = get_current_user() try: user_transactions = UserTransactionsList.get(user) transaction = user_transactions.get_transaction(transaction_id) return {'ok': True, 'data': transaction.status()} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except StoreError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)}
class MultiplayerController(BaseController): """ MultiplayerController consists of all the multiplayer methods """ multiplayer_service = ServiceStatus.check_status_decorator('multiplayer') secret = config.get('multiplayer.secret', None) lock = Lock() last_player_id = 0 last_session_id = 0 sessions = {} servers = {} ## ## FRONT CONTROLLER METHODS ## @classmethod @postonly @multiplayer_service @jsonify def create(cls, slug): game = get_game_by_slug(slug) if not game: response.status_int = 404 return {'ok': False, 'msg': 'Unknown game.'} try: num_slots = int(request.params['slots']) _ = request.params['gameSessionId'] # Check for compatibility with gamesite API which does use this except (KeyError, ValueError): response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} with cls.lock: cls.last_player_id += 1 player_id = str(cls.last_player_id) sessions = cls.sessions cls.last_session_id += 1 session_id = str(cls.last_session_id) server_address = None secret = None if cls.secret is not None: stale_time = time() - 80 for ip, server in cls.servers.iteritems(): if stale_time < server.updated: server_address = '%s:%d' % (ip, server.port) secret = cls.secret break session = MultiplayerSession(session_id, slug, num_slots, server_address, secret) LOG.info('Created session %s (%d slots)', session_id, num_slots) sessions[session_id] = session request_ip = get_remote_addr(request) session.add_player(player_id, request_ip) LOG.info('Player %s joins session %s', player_id, session_id) info = {'server': session.get_player_address(request.host, request_ip, player_id), 'sessionid': session_id, 'playerid': player_id, 'numplayers': session.get_num_players()} return {'ok': True, 'data': info} @classmethod @postonly @multiplayer_service @jsonify def join(cls): params = request.params try: session_id = params['session'] _ = request.params['gameSessionId'] # Check for compatibility with gamesite API which does use this except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} try: session = cls.sessions[session_id] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} with cls.lock: session.update_status() request_ip = get_remote_addr(request) player_id = params.get('player', None) if player_id is None: cls.last_player_id += 1 player_id = str(cls.last_player_id) else: stored_ip = session.get_player_ip(player_id) if stored_ip is not None and request_ip != stored_ip: response.status_int = 401 return {'ok': False} if session.can_join(player_id): session.add_player(player_id, request_ip) LOG.info('Player %s joins session %s', player_id, session_id) info = {'server': session.get_player_address(request.host, request_ip, player_id), 'sessionid': session_id, 'playerid': player_id, 'numplayers': session.get_num_players()} return {'ok': True, 'data': info} response.status_int = 409 return {'ok': False, 'msg': 'No slot available.'} @classmethod @postonly @multiplayer_service @jsonify def join_any(cls, slug): params = request.params try: _ = params['gameSessionId'] # Check for compatibility with gamesite API which does use this except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing game information.'} game = get_game_by_slug(slug) if not game: response.status_int = 404 return {'ok': False, 'msg': 'Unknown game.'} with cls.lock: cls.last_player_id += 1 player_id = str(cls.last_player_id) sessions = cls.sessions session = session_id = None for existing_session in sessions.itervalues(): if existing_session.game == slug: existing_session.update_status() if existing_session.can_join(player_id): session = existing_session session_id = existing_session.session_id break if session is not None: request_ip = get_remote_addr(request) session.add_player(player_id, request_ip) LOG.info('Player %s joins session %s', player_id, session_id) info = {'server': session.get_player_address(request.host, request_ip, player_id), 'sessionid': session_id, 'playerid': player_id, 'numplayers': session.get_num_players()} else: # No session to join info = {} return {'ok': True, 'data': info} @classmethod @postonly @multiplayer_service @jsonify def leave(cls): params = request.params try: session_id = params['session'] player_id = params['player'] _ = params['gameSessionId'] # Check for compatibility with gamesite API which does use this except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} sessions = cls.sessions try: session = sessions[session_id] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} with cls.lock: if session.has_player(player_id): request_ip = get_remote_addr(request) stored_ip = session.get_player_ip(player_id) if stored_ip is not None and request_ip != stored_ip: response.status_int = 401 return {'ok': False} LOG.info('Player %s leaving session %s', player_id, session_id) session.remove_player(player_id) cls._clean_empty_sessions() return {'ok': True} @classmethod @postonly @multiplayer_service @jsonify def make_public(cls): params = request.params try: session_id = params['session'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} sessions = cls.sessions try: session = sessions[session_id] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} session.public = True return {'ok': True} @classmethod @multiplayer_service @jsonify def list_all(cls): request_host = request.host sessions = [] for session in cls.sessions.itervalues(): session.update_status() sessions.append(session.get_info(request_host)) return {'ok': True, 'data': sessions} @classmethod @jsonify def list(cls, slug): game = get_game_by_slug(slug) if not game: response.status_int = 404 return {'ok': False, 'msg': 'Unknown game.'} request_host = request.host sessions = [] for session in cls.sessions.itervalues(): if session.game == slug: session.update_status() sessions.append(session.get_info(request_host)) return {'ok': True, 'data': sessions} @classmethod @multiplayer_service @jsonify def read(cls): params = request.params try: session_id = params['session'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} try: session = cls.sessions[session_id] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} session.update_status() return {'ok': True, 'data': session.get_info(request.host)} @classmethod @postonly @multiplayer_service @jsonify def register(cls): remote_addr = get_remote_addr(request) try: params = request.params host = params.get('host', remote_addr) hmac = params['hmac'] server = MultiplayerServer(params) except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing server information.'} except ValueError: response.status_int = 400 return {'ok': False, 'msg': 'Incorrect server information.'} calculated_hmac = _calculate_registration_hmac(cls.secret, remote_addr) if hmac != calculated_hmac: response.status_int = 400 return {'ok': False, 'msg': 'Invalid server information.'} LOG.info('Multiplayer server registered from %s as %s:%d', remote_addr, host, server.port) cls.servers[host] = server return {'ok': True} @classmethod @postonly @multiplayer_service @jsonify def heartbeat(cls): remote_addr = get_remote_addr(request) try: params = request.params host = params.get('host', remote_addr) num_players = params.get('numplayers') hmac = params['hmac'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing server information.'} calculated_hmac = _calculate_heartbeat_hmac(cls.secret, remote_addr, num_players, None) if hmac != calculated_hmac: response.status_int = 400 return {'ok': False, 'msg': 'Invalid server information.'} try: server = cls.servers[host] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown server IP.'} try: server.update(request.params) except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing server information.'} except ValueError: response.status_int = 400 return {'ok': False, 'msg': 'Incorrect server information.'} #LOG.info('%s: %s', host, str(server)) return {'ok': True} @classmethod @postonly @multiplayer_service @jsonify def unregister(cls): remote_addr = get_remote_addr(request) params = request.params host = params.get('host', remote_addr) try: hmac = params['hmac'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} calculated_hmac = _calculate_registration_hmac(cls.secret, remote_addr) if hmac != calculated_hmac: response.status_int = 400 return {'ok': False, 'msg': 'Invalid server information.'} try: server = cls.servers[host] del cls.servers[host] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown server IP.'} LOG.info('Multiplayer server unregistered from %s:%d', host, server.port) return {'ok': True} @classmethod @postonly @multiplayer_service @jsonify def client_leave(cls): remote_addr = get_remote_addr(request) params = request.params try: session_id = params['session'] player_id = params['client'] hmac = params['hmac'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} calculated_hmac = _calculate_client_hmac(cls.secret, remote_addr, session_id, player_id) if hmac != calculated_hmac: response.status_int = 400 return {'ok': False, 'msg': 'Invalid server information.'} sessions = cls.sessions try: session = sessions[session_id] except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} with cls.lock: if session.has_player(player_id): request_ip = get_remote_addr(request) stored_ip = session.get_player_ip(player_id) if stored_ip is not None and request_ip != stored_ip: response.status_int = 401 return {'ok': False} LOG.info('Player %s left session %s', player_id, session_id) session.remove_player(player_id) cls._clean_empty_sessions() return {'ok': True} @classmethod @postonly @multiplayer_service @jsonify def delete_session(cls): remote_addr = get_remote_addr(request) params = request.params try: session_id = params['session'] hmac = params['hmac'] except KeyError: response.status_int = 400 return {'ok': False, 'msg': 'Missing session information.'} calculated_hmac = _calculate_session_hmac(cls.secret, remote_addr, session_id) if hmac != calculated_hmac: response.status_int = 400 return {'ok': False, 'msg': 'Invalid server information.'} try: with cls.lock: del cls.sessions[session_id] LOG.info('Deleted empty session: %s', session_id) except KeyError: response.status_int = 404 return {'ok': False, 'msg': 'Unknown session.'} return {'ok': True} @classmethod def _clean_empty_sessions(cls): # Needed because of merges sessions = cls.sessions to_delete = [session_id for session_id, existing_session in sessions.iteritems() if 0 == existing_session.get_num_players()] for session_id in to_delete: LOG.info('Deleting empty session: %s', session_id) del sessions[session_id] # Internal API used by internal mp server @classmethod def remove_player(cls, session_id, player_id): try: sessions = cls.sessions session = sessions[session_id] with cls.lock: if session.has_player(player_id): LOG.info('Player %s left session %s', player_id, session_id) session.remove_player(player_id) cls._clean_empty_sessions() except KeyError: pass
class LeaderboardsController(BaseController): """ LeaderboardsController consists of all the Leaderboards methods """ leaderboards_service = ServiceStatus.check_status_decorator('leaderboards') max_top_size = 32 max_near_size = 32 max_page_size = 64 @classmethod @leaderboards_service @jsonify def read_meta(cls, slug): game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with that slug'} try: leaderboards = LeaderboardsList.load(game) return {'ok': True, 'data': leaderboards.read_meta()} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @postonly @leaderboards_service @jsonify def reset_meta(cls): LeaderboardsList.reset() return {'ok': True} @classmethod @leaderboards_service @jsonify def read_overview(cls, slug): game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with that slug'} try: leaderboards = LeaderboardsList.get(game) return {'ok': True, 'data': leaderboards.read_overview(get_current_user())} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @leaderboards_service @jsonify def read_aggregates(cls, slug): game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with that slug'} try: leaderboards = LeaderboardsList.get(game) return {'ok': True, 'data': leaderboards.read_aggregates()} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @leaderboards_service @jsonify def read_expanded(cls, slug, key): game = get_game_by_slug(slug) if game is None: response.status_int = 404 return {'ok': False, 'msg': 'No game with that slug'} params = request.GET method_type = params.get('type', 'top') def get_size(default_size, max_size): try: size = int(params.get('size', default_size)) if size <= 0 or size > max_size: raise BadRequest('size must be a positive integer smaller than %d' % max_size) except ValueError: raise BadRequest('size must be a positive integer smaller than %d' % max_size) return size try: leaderboards = LeaderboardsList.get(game) is_above = (method_type == 'above') if method_type == 'below' or is_above: try: score = float(params.get('score')) score_time = float(params.get('time', 0)) if isinf(score) or isnan(score) or isinf(score_time) or isnan(score_time): response.status_int = 400 return { 'ok': False, 'msg': 'Score or time are incorrectly formated' } except (TypeError, ValueError): response.status_int = 400 return {'ok': False, 'msg': 'Score or time parameter missing'} return {'ok': True, 'data': leaderboards.get_page(key, get_current_user(), get_size(5, cls.max_page_size), is_above, score, score_time)} if method_type == 'near': return {'ok': True, 'data': leaderboards.get_near(key, get_current_user(), get_size(9, cls.max_near_size))} else: # method_type == 'top' return {'ok': True, 'data': leaderboards.get_top_players(key, get_current_user(), get_size(9, cls.max_top_size))} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @leaderboards_service @secure_post def set(cls, key, params=None): session = cls._get_gamesession(params) try: leaderboards = LeaderboardsList.get(session.game) score = float(params['score']) if isinf(score): response.status_int = 400 return {'ok': False, 'msg': '"score" must be a finite number'} if score < 0: response.status_int = 400 return {'ok': False, 'msg': '"score" cannot be a negative number'} return {'ok': True, 'data': leaderboards.set(key, session.user, score)} except (TypeError, ValueError): response.status_int = 400 return {'ok': False, 'data': 'Score is missing or incorrectly formated'} except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} @classmethod @leaderboards_service @jsonify def remove_all(cls, slug): # This is for testing only and is not present on the Hub or Gamesite game = get_game_by_slug(slug) if not game: response.status_int = 404 return {'ok': False, 'msg': 'No game with that slug exists'} try: leaderboards = LeaderboardsList.get(game) leaderboards.remove_all() except ValidationException as e: response.status_int = 400 return {'ok': False, 'msg': str(e)} except LeaderboardError as e: response.status_int = e.response_code return {'ok': False, 'msg': str(e)} return {'ok': True}
def __init_controllers(): ServiceStatus.set_ok('userdata') ServiceStatus.set_ok('gameProfile') ServiceStatus.set_ok('leaderboards') ServiceStatus.set_ok('gameSessions') ServiceStatus.set_ok('badges') ServiceStatus.set_ok('profiles') ServiceStatus.set_ok('multiplayer') ServiceStatus.set_ok('customMetrics') ServiceStatus.set_ok('store') ServiceStatus.set_ok('datashare') ServiceStatus.set_ok('notifications') GameSessionList.get_instance().purge_sessions()
class DatashareController(BaseController): """ DataShareController consists of all the datashare methods """ datashare_service = ServiceStatus.check_status_decorator('datashare') game_session_list = GameSessionList.get_instance() # Testing only - Not available on the Gamesite @classmethod @postonly @datashare_service @jsonify def remove_all(cls, slug): game = get_game_by_slug(slug) if game is None: raise NotFound('No game with slug %s' % slug) DataShareList.get(game).remove_all() return {'ok': True} @classmethod @postonly @datashare_service @jsonify def create(cls, slug): game = get_game_by_slug(slug) datashare = DataShareList.get(game).create_datashare( get_current_user()) return {'ok': True, 'data': {'datashare': datashare.summary_dict()}} @classmethod @postonly @datashare_service @jsonify def join(cls, slug, datashare_id): game = get_game_by_slug(slug) datashare = DataShareList.get(game).get(datashare_id) datashare.join(get_current_user()) return {'ok': True, 'data': {'users': datashare.users}} @classmethod @postonly @datashare_service @jsonify def leave(cls, slug, datashare_id): game = get_game_by_slug(slug) DataShareList.get(game).leave_datashare(get_current_user(), datashare_id) return {'ok': True} @classmethod @datashare_service @secure_post def set_properties(cls, slug, datashare_id, params=None): game = get_game_by_slug(slug) datashare = DataShareList.get(game).get(datashare_id) if 'joinable' in params: try: joinable = asbool(params['joinable']) except ValueError: raise BadRequest('Joinable must be a boolean value') datashare.set_joinable(get_current_user(), joinable) return {'ok': True} @classmethod @datashare_service @jsonify def find(cls, slug): game = get_game_by_slug(slug) username = request.params.get('username') datashares = DataShareList.get(game).find(get_current_user(), username_to_find=username) return { 'ok': True, 'data': { 'datashares': [datashare.summary_dict() for datashare in datashares] } } @classmethod @datashare_service @secure_get def read(cls, datashare_id, params=None): session = cls._get_gamesession(params) datashare = DataShareList.get(session.game).get(datashare_id) datashare_keys = datashare.get_keys(session.user) return {'ok': True, 'data': {'keys': datashare_keys}} @classmethod @datashare_service @secure_get def read_key(cls, datashare_id, key, params=None): session = cls._get_gamesession(params) datashare = DataShareList.get(session.game).get(datashare_id) datashare_key = datashare.get(session.user, key) return {'ok': True, 'data': datashare_key} @classmethod @datashare_service @secure_post def set_key(cls, datashare_id, key, params=None): session = cls._get_gamesession(params) datashare = DataShareList.get(session.game).get(datashare_id) value = params.get('value') new_token = datashare.set(session.user, key, value) return {'ok': True, 'data': {'token': new_token}} @classmethod @datashare_service @secure_post def compare_and_set_key(cls, datashare_id, key, params=None): session = cls._get_gamesession(params) datashare = DataShareList.get(session.game).get(datashare_id) value = params.get('value') token = params.get('token') try: new_token = datashare.compare_and_set(session.user, key, value, token) return {'ok': True, 'data': {'wasSet': True, 'token': new_token}} except CompareAndSetInvalidToken: return {'ok': True, 'data': {'wasSet': False}}
class UserdataController(BaseController): """ UserdataController consists of all the Userdata methods """ game_session_list = GameSessionList.get_instance() userdata_service = ServiceStatus.check_status_decorator('userdata') @classmethod @userdata_service @secure_get def read_keys(cls, params=None): _set_json_headers(response.headers) userdata = UserData(cls._get_gamesession(params)) return {'ok': True, 'keys': userdata.get_keys()} @classmethod @userdata_service @secure_get def exists(cls, key, params=None): _set_json_headers(response.headers) userdata = UserData(cls._get_gamesession(params)) return {'ok': True, 'exists': userdata.exists(key)} @classmethod @userdata_service @secure_get def read(cls, key, params=None): _set_json_headers(response.headers) userdata = UserData(cls._get_gamesession(params)) try: value = userdata.get(key) except UserDataKeyError: response.status_int = 404 return {'ok': False, 'msg': 'Key does not exist'} else: return {'ok': True, 'value': value} @classmethod @userdata_service @secure_post def set(cls, key, params=None): userdata = UserData(cls._get_gamesession(params)) value = params['value'] userdata.set(key, value) return {'ok': True} @classmethod @userdata_service @secure_post def remove(cls, key, params=None): userdata = UserData(cls._get_gamesession(params)) try: userdata.remove(key) except UserDataKeyError: response.status_int = 404 return {'ok': False, 'msg': 'Key does not exist'} else: return {'ok': True} @classmethod @userdata_service @secure_post def remove_all(cls, params=None): userdata = UserData(cls._get_gamesession(params)) userdata.remove_all() return {'ok': True}
class CustommetricsController(BaseController): custommetrics_service = ServiceStatus.check_status_decorator('customMetrics') @classmethod @custommetrics_service @secure_post def add_event(cls, slug, params=None): # Only a validation simulation! Custom events are only tracked on the game site. try: session = cls.game_session_list.get_session(params['gameSessionId']) except (KeyError, TypeError): raise BadRequest('Invalid game session id') game = session.game if game is None: raise ApiException('No game with that slug') if slug != game.slug: raise BadRequest('Slug and game session do not match') try: event_key = params['key'] except (TypeError, KeyError): raise BadRequest('Event key missing') try: event_value = params['value'] except (TypeError, KeyError): raise BadRequest('Event value missing') del params['value'] try: event_key, event_value = _validate_event(event_key, event_value) except ValueError as e: raise BadRequest(e.message) # If reaches this point, assume success return {'ok': True, 'data': {'msg': 'Added "' + str(event_value) + '" for "' + event_key + '" ' \ '(Simulation only - Custom events are only tracked on the game site)'}} @classmethod @custommetrics_service @secure_post def add_event_batch(cls, slug, params=None): # Only a validation simulation! Custom events are only tracked on the game site. try: session = cls.game_session_list.get_session(params['gameSessionId']) except (KeyError, TypeError): raise BadRequest('Invalid game session id') game = session.game if game is None: raise ApiException('No game with that slug') if slug != game.slug: raise BadRequest('Slug and game session do not match') try: event_batch = params['batch'] except (TypeError, KeyError): raise BadRequest('Event batch missing') del params['batch'] if not isinstance(event_batch, list): raise BadRequest('Event batch must be an array of events') for event in event_batch: try: event_key = event['key'] except (TypeError, KeyError): raise BadRequest('Event key missing') try: event_value = event['value'] except (TypeError, KeyError): raise BadRequest('Event value missing') try: event_key, event_value = _validate_event(event_key, event_value) except ValueError as e: raise BadRequest(e.message) try: event_time = float(event['timeOffset']) except (TypeError, KeyError): raise BadRequest('Event time offset missing') except ValueError: raise BadRequest('Event time offset should be a float') if event_time > 0: raise BadRequest('Event time offsets should be <= 0 to represent older events') # If reaches this point, assume success return {'ok': True, 'data': {'msg': 'Added %d events ' \ '(Simulation only - Custom events are only tracked on the game site)' % len(event_batch)}}
def __init_controllers(): ServiceStatus.set_ok('userdata') ServiceStatus.set_ok('gameProfile') ServiceStatus.set_ok('leaderboards') ServiceStatus.set_ok('gameSessions') ServiceStatus.set_ok('badges') ServiceStatus.set_ok('profiles') ServiceStatus.set_ok('multiplayer') ServiceStatus.set_ok('customMetrics') ServiceStatus.set_ok('store') GameSessionList.get_instance().purge_sessions()
class BadgesController(BaseController): """ BadgesController consists of all the badges methods """ badges_service = ServiceStatus.check_status_decorator('badges') #list badges for a given user (the username is taken from the environment if it's not passed as a parameter) @classmethod @jsonify def badges_user_list(cls, slug=None): try: game = get_game_by_slug(slug) if game is None: raise ApiException('No game with that slug') # get the user from the environment # get a user model (simulation) user = get_current_user() # try to get a user_id from the context badges_obj = Badges.get_singleton(game) badges = badges_obj.badges badges_total_dict = dict((b['key'], b.get('total')) for b in badges) userbadges = badges_obj.find_userbadges_by_user(user.username) for key, userbadge in userbadges.iteritems(): del userbadge['username'] try: total = badges_total_dict[key] except KeyError: # the badge has been deleted or its key renamed so we just skip it continue userbadge['total'] = total userbadge['achieved'] = (userbadge['current'] >= total) response.status_int = 200 return {'ok': True, 'data': userbadges.values()} except BadgesUnsupportedException: return {'ok': False, 'data': []} except ApiException as message: response.status_int = 404 return {'ok': False, 'msg': str(message)} @classmethod @badges_service @jsonify def badges_list(cls, slug): try: game = get_game_by_slug(slug) if game is None: raise ApiException('No game with that slug') badges = Badges.get_singleton(game).badges # Patch any unset total values in the response (to be consistent with the hub and game site) for badge in badges: if 'total' not in badge: badge['total'] = None if 'predescription' not in badge: badge['predescription'] = None return {'ok': True, 'data': badges} except BadgesUnsupportedException: return {'ok': False, 'data': []} except ApiException as message: response.status_int = 404 return {'ok': False, 'msg': str(message)} except ScannerError as message: response.status_int = 404 return {'ok': False, 'msg': 'Could not parse YAML file. %s' % (message)} @classmethod @badges_service @secure_post # add a badge to a user (gets passed # a badge and a current level over POST, # the username is taken from the environment) def badges_user_add(cls, slug, params=None): try: session = cls._get_gamesession(params) game = session.game if game is None: raise ApiException('No game with that slug') badge_key = params['badge_key'] if not badge_key: raise ApiException('Must specify a badge_key to add.') # we have a badge_key now try to see if that badge exists badges_obj = Badges.get_singleton(game) badge = badges_obj.get_badge(badge_key) if not badge: raise ApiException('Badge name %s was not found.' % badge_key) if not ('image' in badge) or not badge['image']: badge['image'] = '/static/img/badge-46x46.png' # Use the badge['key'] property because badge_key is unicode ub = {'username': session.user.username, 'badge_key': badge['key']} badge_total = badge.get('total') total = badge_total or 1.0 current = 0 if 'current' in params: try: current = float(int(params['current'])) except (ValueError, TypeError): response.status_int = 400 return {'ok': False, 'msg': '\'current\' must be a integer'} if not current: current = total ub['current'] = current userbadge = badges_obj.get_userbadge(session.user.username, badge_key) Badges.get_singleton(game).upsert_badge(ub) if current >= total and (not userbadge or userbadge.get('current', 0) < total): achieved = True else: achieved = False response.status_int = 200 return {'ok': True, 'data': { 'current': current, 'total': badge_total, 'badge_key': badge_key, 'achieved': achieved }} except BadgesUnsupportedException: response.status_int = 404 return {'ok': False, 'msg': 'Badges are unsupported for this game'} except ApiException as message: response.status_int = 404 return {'ok': False, 'msg': str(message)}