From 30874697cc9be587300c09199c90ba7ce486da7d Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Fri, 21 Oct 2016 12:14:37 -0400 Subject: [PATCH] Rework the API calls for tournaments, add an index for getting tournaments registered by a given account --- libraries/app/database_api.cpp | 60 +++++------------ .../app/include/graphene/app/database_api.hpp | 15 ++--- libraries/chain/db_init.cpp | 3 +- .../graphene/chain/tournament_object.hpp | 29 ++++++++ libraries/chain/tournament_evaluator.cpp | 8 ++- libraries/chain/tournament_object.cpp | 67 +++++++++++++++++++ .../wallet/include/graphene/wallet/wallet.hpp | 3 +- libraries/wallet/wallet.cpp | 17 ++--- 8 files changed, 137 insertions(+), 65 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b8c0c280..be234b20 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -139,8 +139,8 @@ class database_api_impl : public std::enable_shared_from_this vector get_blinded_balances( const flat_set& commitments )const; // Tournaments - vector get_upcoming_tournaments(fc::optional account_filter, uint32_t limit)const; - vector get_active_tournaments(fc::optional account_filter, uint32_t limit)const; + vector get_tournaments_in_state(tournament_state state, uint32_t limit) const; + vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; //private: @@ -1769,26 +1769,20 @@ vector database_api_impl::get_blinded_balances( const fl // Tournament methods // // // ////////////////////////////////////////////////////////////////////// - -vector database_api::get_upcoming_tournaments(fc::optional account_filter, uint32_t limit)const +vector database_api::get_tournaments_in_state(tournament_state state, uint32_t limit) const { - return my->get_upcoming_tournaments(account_filter, limit); + return my->get_tournaments_in_state(state, limit); } -vector database_api_impl::get_upcoming_tournaments(fc::optional account_filter, uint32_t limit)const +vector database_api_impl::get_tournaments_in_state(tournament_state state, uint32_t limit) const { vector result; const auto& registration_deadline_index = _db.get_index_type().indices().get(); - const auto range = registration_deadline_index.equal_range(boost::make_tuple(tournament_state::accepting_registrations)); + const auto range = registration_deadline_index.equal_range(boost::make_tuple(state)); for (const tournament_object& tournament_obj : boost::make_iterator_range(range.first, range.second)) { - if (tournament_obj.options.whitelist.empty() || - !account_filter || - tournament_obj.options.whitelist.find(*account_filter) != tournament_obj.options.whitelist.end()) - { - result.emplace_back(tournament_obj); - subscribe_to_item( tournament_obj.id ); - } + result.emplace_back(tournament_obj); + subscribe_to_item( tournament_obj.id ); if (result.size() >= limit) break; @@ -1796,39 +1790,21 @@ vector database_api_impl::get_upcoming_tournaments(fc::option return result; } -vector database_api::get_active_tournaments(fc::optional account_filter, uint32_t limit)const +vector database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const { - return my->get_active_tournaments(account_filter, limit); + return my->get_registered_tournaments(account_filter, limit); } -vector database_api_impl::get_active_tournaments(fc::optional account_filter, uint32_t limit)const +vector database_api_impl::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const { - vector result; - const auto& start_time_index = _db.get_index_type().indices().get(); + const auto& tournament_details_idx = _db.get_index_type(); + const auto& tournament_details_primary_idx = dynamic_cast&>(tournament_details_idx); + const auto& players_idx = tournament_details_primary_idx.get_secondary_index(); - const auto begin = start_time_index.lower_bound(boost::make_tuple(tournament_state::awaiting_start)); - const auto end = start_time_index.upper_bound(boost::make_tuple(tournament_state::in_progress)); - for (const tournament_object& tournament_obj : boost::make_iterator_range(begin, end)) - { - if (account_filter) - { - const tournament_details_object& tournament_details_obj = tournament_obj.tournament_details_id(_db); - if (tournament_details_obj.registered_players.find(*account_filter) != tournament_details_obj.registered_players.end()) - { - result.emplace_back(tournament_obj); - subscribe_to_item( tournament_obj.id ); - } - } - else - { - result.emplace_back(tournament_obj); - subscribe_to_item( tournament_obj.id ); - } - - if (result.size() >= limit) - break; - } - return result; + vector tournament_ids = players_idx.get_registered_tournaments_for_account(account_filter); + if (tournament_ids.size() >= limit) + tournament_ids.resize(limit); + return tournament_ids; } ////////////////////////////////////////////////////////////////////// diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 22dc535e..60d1c44f 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -553,17 +553,14 @@ class database_api // Tournaments // ///////////////// /** - * @param account_filter if provided, this will only return tournaments the given account is - * allowed to join (public tournaments or tournaments the account is whitelisted for) - * @return the list of tournaments that are still accepting new registrations + * @return the list of tournaments in the given state */ - vector get_upcoming_tournaments(fc::optional account_filter, uint32_t limit)const; + vector get_tournaments_in_state(tournament_state state, uint32_t limit) const; /** - * @return the list of tournaments that are either in-progress or fully-registered and just waiting on their start - * time to arrive + * @return the list of tournaments that a given account is registered to play in */ - vector get_active_tournaments(fc::optional account_filter, uint32_t limit)const; + vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; private: std::shared_ptr< database_api_impl > my; @@ -667,6 +664,6 @@ FC_API(graphene::app::database_api, (get_blinded_balances) // Tournaments - (get_upcoming_tournaments) - (get_active_tournaments) + (get_tournaments_in_state) + (get_registered_tournaments) ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2f9c3b8f..12da3670 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -208,7 +208,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + auto tournament_details_idx = add_index< primary_index >(); + tournament_details_idx->add_secondary_index(); add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index df144f7d..355e454b 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -29,6 +29,9 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = tournament_details_object_type; + /// the tournament object for which this is the details + tournament_id_type tournament_id; + /// List of players registered for this tournament flat_set registered_players; @@ -197,9 +200,35 @@ namespace graphene { namespace chain { return s; } + /** + * @brief This secondary index will allow a reverse lookup of all tournaments + * a particular account has registered for. This will be attached + * to the tournament details index because the registrations are contained + * in the tournament details object, but it will index the tournament ids + * since that is most useful to the GUI. + */ + class tournament_players_index : public secondary_index + { + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + /** given an account, map it to the set of tournaments in which that account is registered as a player */ + map< account_id_type, flat_set > account_to_joined_tournaments; + + vector get_registered_tournaments_for_account( const account_id_type& a )const; + protected: + + flat_set before_account_ids; + }; + + } } FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object), + (tournament_id) (registered_players) (payers) (matches)) diff --git a/libraries/chain/tournament_evaluator.cpp b/libraries/chain/tournament_evaluator.cpp index 1c26c092..12e89661 100644 --- a/libraries/chain/tournament_evaluator.cpp +++ b/libraries/chain/tournament_evaluator.cpp @@ -84,6 +84,12 @@ namespace graphene { namespace chain { t.tournament_details_id = tournament_details.id; }); + // TODO: look up how to do this in the initial create + db().modify(tournament_details, [&]( tournament_details_object& a ) { + a.tournament_id = new_tournament.id; + }); + + fc_ilog(fc::logger::get("tournament"), "Created tournament ${id} with details id ${details_id}", ("id", new_tournament.id)("details_id", tournament_details.id)); @@ -107,7 +113,7 @@ namespace graphene { namespace chain { "Registration deadline has already passed"); FC_ASSERT(_tournament_obj->options.whitelist.empty() || - _tournament_obj->options.whitelist.find(op.player_account_id) == _tournament_obj->options.whitelist.end(), + _tournament_obj->options.whitelist.find(op.player_account_id) != _tournament_obj->options.whitelist.end(), "Player is not on the whitelist for this tournament"); FC_ASSERT(_tournament_details_obj->registered_players.find(op.player_account_id) == _tournament_details_obj->registered_players.end(), diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index afd965a8..cad37c5c 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -479,6 +479,73 @@ namespace graphene { namespace chain { return fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size()); } + + vector tournament_players_index::get_registered_tournaments_for_account( const account_id_type& a )const + { + auto iter = account_to_joined_tournaments.find(a); + if (iter != account_to_joined_tournaments.end()) + return vector(iter->second.begin(), iter->second.end()); + return vector(); + } + + void tournament_players_index::object_inserted(const object& obj) + { + assert( dynamic_cast(&obj) ); // for debug only + const tournament_details_object& details = static_cast(obj); + + for (const account_id_type& account_id : details.registered_players) + account_to_joined_tournaments[account_id].insert(details.tournament_id); + } + + void tournament_players_index::object_removed(const object& obj) + { + assert( dynamic_cast(&obj) ); // for debug only + const tournament_details_object& details = static_cast(obj); + + for (const account_id_type& account_id : details.registered_players) + { + auto iter = account_to_joined_tournaments.find(account_id); + if (iter != account_to_joined_tournaments.end()) + iter->second.erase(details.tournament_id); + } + } + + void tournament_players_index::about_to_modify(const object& before) + { + assert( dynamic_cast(&before) ); // for debug only + const tournament_details_object& details = static_cast(before); + before_account_ids = details.registered_players; + } + + void tournament_players_index::object_modified(const object& after) + { + assert( dynamic_cast(&after) ); // for debug only + const tournament_details_object& details = static_cast(after); + + { + vector newly_registered_players(details.registered_players.size()); + auto end_iter = std::set_difference(details.registered_players.begin(), details.registered_players.end(), + before_account_ids.begin(), before_account_ids.end(), + newly_registered_players.begin()); + newly_registered_players.resize(end_iter - newly_registered_players.begin()); + for (const account_id_type& account_id : newly_registered_players) + account_to_joined_tournaments[account_id].insert(details.tournament_id); + } + + { + vector newly_unregistered_players(before_account_ids.size()); + auto end_iter = std::set_difference(before_account_ids.begin(), before_account_ids.end(), + details.registered_players.begin(), details.registered_players.end(), + newly_unregistered_players.begin()); + newly_unregistered_players.resize(end_iter - newly_unregistered_players.begin()); + for (const account_id_type& account_id : newly_unregistered_players) + { + auto iter = account_to_joined_tournaments.find(account_id); + if (iter != account_to_joined_tournaments.end()) + iter->second.erase(details.tournament_id); + } + } + } } } // graphene::chain namespace fc { diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index e2b04909..9bcc6900 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1422,10 +1422,9 @@ class wallet_api signed_transaction tournament_join( string payer_account, string player_account, tournament_id_type tournament_id, string buy_in_amount, string buy_in_asset_symbol, bool broadcast = false ); /** Get a list of upcoming tournaments - * @param player_accounts if non-empty, only return tournaments for which at least one of the named players is eligible. If empty, return all tournaments * @param limit the number of tournaments to return */ - vector get_upcoming_tournaments(optional player_accounts, uint32_t limit); + vector get_upcoming_tournaments(uint32_t limit); /** Get specific information about a tournament * @param tournament_id the ID of the tournament diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2ecc86e7..b4a19c6f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -951,12 +951,12 @@ public: game_cache.clear(); for (const account_object& my_account : _wallet.my_accounts) { - std::vector tournaments = _remote_db->get_active_tournaments(my_account.id, 100); - std::vector tournament_ids; - for (const tournament_object& tournament : tournaments) + std::vector tournament_ids = _remote_db->get_registered_tournaments(my_account.id, 100); + for (const tournament_id_type& tournament_id : tournament_ids) { try { + tournament_object tournament = get_object(tournament_id); auto insert_result = tournament_cache.insert(tournament); if (insert_result.second) { @@ -967,10 +967,10 @@ public: } catch (const fc::exception& e) { - edump((e)(tournament)); + edump((e)(tournament_id)); } } - if (!tournaments.empty()) + if (!tournament_ids.empty()) ilog("Account ${my_account} is registered for tournaments: ${tournaments}", ("my_account", my_account.name)("tournaments", tournament_ids)); else ilog("Account ${my_account} is not registered for any tournaments", ("my_account", my_account.name)); @@ -4552,12 +4552,9 @@ signed_transaction wallet_api::tournament_join( string payer_account, return my->sign_transaction( tx, broadcast ); } -vector wallet_api::get_upcoming_tournaments(fc::optional player_account, uint32_t limit) +vector wallet_api::get_upcoming_tournaments(uint32_t limit) { - fc::optional player_account_id; - if (player_account) - player_account_id = get_account(*player_account).id; - return my->_remote_db->get_upcoming_tournaments(player_account_id, limit); + return my->_remote_db->get_tournaments_in_state(tournament_state::accepting_registrations, limit); } tournament_object wallet_api::get_tournament(tournament_id_type id)