diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 5edb63fe..e56d0126 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -77,6 +77,7 @@ add_library( graphene_chain tournament_evaluator.cpp tournament_object.cpp match_object.cpp + game_object.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp confidential_evaluator.cpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 070ec618..80005410 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include diff --git a/libraries/chain/game_object.cpp b/libraries/chain/game_object.cpp new file mode 100644 index 00000000..b13e9410 --- /dev/null +++ b/libraries/chain/game_object.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + + namespace msm = boost::msm; + namespace mpl = boost::mpl; + + namespace + { + // Events + struct initiate_game + { + database& db; + vector players; + initiate_game(database& db, const vector& players) : + db(db), players(players) + {} + }; + + struct game_complete + { + database& db; + game_id_type game_id; + game_complete(database& db, game_id_type game_id) : db(db), game_id(game_id) {}; + }; + + struct game_state_machine_ : public msm::front::state_machine_def + { + // disable a few state machine features we don't use for performance + typedef int no_exception_thrown; + typedef int no_message_queue; + + // States + struct waiting_on_previous_gamees : public msm::front::state<>{}; + struct game_in_progress : public msm::front::state<> + { + void on_entry(const initiate_game& event, game_state_machine_& fsm) + { + game_object& game = *fsm.game_obj; + + fc_ilog(fc::logger::get("tournament"), + "game ${id} is now in progress", + ("id", game.id)); + } + }; + struct game_complete : public msm::front::state<> + { + void on_entry(const game_complete& event, game_state_machine_& fsm) + { + fc_ilog(fc::logger::get("tournament"), + "game ${id} is complete", + ("id", fsm.game_obj->id)); + } + void on_entry(const initiate_game& event, game_state_machine_& fsm) + { + game_object& game = *fsm.game_obj; + fc_ilog(fc::logger::get("tournament"), + "game ${id} is complete, it was a buy", + ("id", game)); + } + }; + typedef waiting_on_previous_gamees initial_state; + + typedef game_state_machine_ x; // makes transition table cleaner + + // Guards + bool was_final_game(const game_complete& event) + { + fc_ilog(fc::logger::get("tournament"), + "In was_final_game guard, returning ${value}", + ("value", false));// game_obj->registered_players == game_obj->options.number_of_players - 1)); + return false; + //return game_obj->registered_players == game_obj->options.number_of_players - 1; + } + + bool game_is_a_buy(const initiate_game& event) + { + return event.players.size() < 2; + } + + void start_next_game(const game_complete& event) + { + fc_ilog(fc::logger::get("tournament"), + "In start_next_game action"); + } + + // Transition table for tournament + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+ + _row < waiting_on_previous_gamees, initiate_game, game_in_progress >, + g_row < waiting_on_previous_gamees, initiate_game, game_complete, &x::game_is_a_buy >, + // +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+ + a_row < game_in_progress, game_complete, game_in_progress, &x::start_next_game >, + g_row < game_in_progress, game_complete, game_complete, &x::was_final_game > + // +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+ + > {}; + + + game_object* game_obj; + game_state_machine_(game_object* game_obj) : game_obj(game_obj) {} + }; + typedef msm::back::state_machine game_state_machine; + } + + class game_object::impl { + public: + game_state_machine state_machine; + + impl(game_object* self) : state_machine(self) {} + }; + + game_object::game_object() : + my(new impl(this)) + { + } + + game_object::game_object(const game_object& rhs) : + graphene::db::abstract_object(rhs), + players(rhs.players), + winners(rhs.winners), + game_details(rhs.game_details), + my(new impl(this)) + { + my->state_machine = rhs.my->state_machine; + my->state_machine.game_obj = this; + } + + game_object& game_object::operator=(const game_object& rhs) + { + //graphene::db::abstract_object::operator=(rhs); + id = rhs.id; + players = rhs.players; + winners = rhs.winners; + game_details = rhs.game_details; + my->state_machine = rhs.my->state_machine; + my->state_machine.game_obj = this; + + return *this; + } + + game_object::~game_object() + { + } + + bool verify_game_state_constants() + { + unsigned error_count = 0; + typedef msm::back::generate_state_set::type all_states; + static char const* filled_state_names[mpl::size::value]; + mpl::for_each > + (msm::back::fill_state_names(filled_state_names)); + for (unsigned i = 0; i < mpl::size::value; ++i) + { + try + { + // this is an approximate test, the state name provided by typeinfo will be mangled, but should + // at least contain the string we're looking for + const char* fc_reflected_value_name = fc::reflector::to_string((game_state)i); + if (!strcmp(fc_reflected_value_name, filled_state_names[i])) + fc_elog(fc::logger::get("game"), + "Error, state string misgame between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}", + ("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name)); + } + catch (const fc::bad_cast_exception&) + { + fc_elog(fc::logger::get("game"), + "Error, no reflection for value ${int_value} in enum game_state", + ("int_value", i)); + ++error_count; + } + } + + return error_count == 0; + } + + game_state game_object::get_state() const + { + static bool state_constants_are_correct = verify_game_state_constants(); + (void)&state_constants_are_correct; + game_state state = (game_state)my->state_machine.current_state()[0]; + + return state; + } + + void game_object::pack_impl(std::ostream& stream) const + { + boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking); + oa << my->state_machine; + } + + void game_object::unpack_impl(std::istream& stream) + { + boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking); + ia >> my->state_machine; + } + +} } // graphene::chain + +namespace fc { + // Manually reflect game_object to variant to properly reflect "state" + void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v) + { + fc_elog(fc::logger::get("tournament"), "In game_obj to_variant"); + elog("In game_obj to_variant"); + fc::mutable_variant_object o; + o("id", game_obj.id) + ("players", game_obj.players) + ("winners", game_obj.winners) + ("game_details", game_obj.game_details) + ("state", game_obj.get_state()); + + v = o; + } + + // Manually reflect game_object to variant to properly reflect "state" + void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj) + { + fc_elog(fc::logger::get("tournament"), "In game_obj from_variant"); + game_obj.id = v["id"].as(); + game_obj.players = v["players"].as >(); + game_obj.winners = v["winners"].as >(); + game_obj.game_details = v["game_details"].as(); + graphene::chain::game_state state = v["state"].as(); + const_cast(game_obj.my->state_machine.current_state())[0] = (int)state; + } +} //end namespace fc + + diff --git a/libraries/chain/include/graphene/chain/game_object.hpp b/libraries/chain/include/graphene/chain/game_object.hpp new file mode 100644 index 00000000..ed4aea0d --- /dev/null +++ b/libraries/chain/include/graphene/chain/game_object.hpp @@ -0,0 +1,125 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + class game_object; +} } + +namespace fc { + void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v); + void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj); +} //end namespace fc + +namespace graphene { namespace chain { + class database; + using namespace graphene::db; + + enum class game_state + { + game_in_progress, + game_complete + }; + + class game_object : public graphene::db::abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = game_object_type; + + match_id_type match_id; + + vector players; + + flat_set winners; + + game_specific_details game_details; + + game_state get_state() const; + + game_object(); + game_object(const game_object& rhs); + ~game_object(); + game_object& operator=(const game_object& rhs); + + // serialization functions: + // for serializing to raw, go through a temporary sstream object to avoid + // having to implement serialization in the header file + template + friend Stream& operator<<( Stream& s, const game_object& game_obj ); + + template + friend Stream& operator>>( Stream& s, game_object& game_obj ); + + friend void ::fc::to_variant(const graphene::chain::game_object& game_obj, fc::variant& v); + friend void ::fc::from_variant(const fc::variant& v, graphene::chain::game_object& game_obj); + + void pack_impl(std::ostream& stream) const; + void unpack_impl(std::istream& stream); + + class impl; + std::unique_ptr my; + }; + + typedef multi_index_container< + game_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > + > game_object_multi_index_type; + typedef generic_index game_index; + + template + inline Stream& operator<<( Stream& s, const game_object& game_obj ) + { + // pack all fields exposed in the header in the usual way + // instead of calling the derived pack, just serialize the one field in the base class + // fc::raw::pack >(s, game_obj); + fc::raw::pack(s, game_obj.id); + fc::raw::pack(s, game_obj.match_id); + fc::raw::pack(s, game_obj.players); + fc::raw::pack(s, game_obj.winners); + fc::raw::pack(s, game_obj.game_details); + + // fc::raw::pack the contents hidden in the impl class + std::ostringstream stream; + game_obj.pack_impl(stream); + std::string stringified_stream(stream.str()); + fc::raw::pack(s, stream.str()); + + return s; + } + + template + inline Stream& operator>>( Stream& s, game_object& game_obj ) + { + // unpack all fields exposed in the header in the usual way + //fc::raw::unpack >(s, game_obj); + fc::raw::unpack(s, game_obj.id); + fc::raw::unpack(s, game_obj.match_id); + fc::raw::unpack(s, game_obj.players); + fc::raw::unpack(s, game_obj.winners); + fc::raw::unpack(s, game_obj.game_details); + + // fc::raw::unpack the contents hidden in the impl class + std::string stringified_stream; + fc::raw::unpack(s, stringified_stream); + std::istringstream stream(stringified_stream); + game_obj.unpack_impl(stream); + + return s; + } + +} } + +FC_REFLECT_ENUM(graphene::chain::game_state, + (game_in_progress) + (game_complete)) + +FC_REFLECT_TYPENAME(graphene::chain::game_object) // manually serialized + + diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index d9250789..f8a04a97 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -119,21 +119,6 @@ namespace graphene { namespace chain { std::unique_ptr my; }; - class game_object : public graphene::db::abstract_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = game_object_type; - - match_id_type match_id; - - vector players; - - flat_set winners; - - game_specific_details game_details; - }; - struct by_registration_deadline {}; struct by_start_time {}; typedef multi_index_container< @@ -159,12 +144,6 @@ namespace graphene { namespace chain { > tournament_details_object_multi_index_type; typedef generic_index tournament_details_index; - typedef multi_index_container< - game_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > > - > game_object_multi_index_type; - typedef generic_index game_index; template inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj ) @@ -232,9 +211,3 @@ FC_REFLECT_ENUM(graphene::chain::tournament_state, (registration_period_expired) (concluded)) -FC_REFLECT_DERIVED(graphene::chain::game_object, (graphene::db::object), - (match_id) - (players) - (winners) - (game_details)) - diff --git a/libraries/chain/match_object.cpp b/libraries/chain/match_object.cpp index 37a60d07..4a146b88 100644 --- a/libraries/chain/match_object.cpp +++ b/libraries/chain/match_object.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include