From 3f8e6bf5f5197a665b9e6da1f7afdc745a325e19 Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 12 Jul 2016 23:43:23 +0100 Subject: [PATCH] More smart pointers, clean up threading a little The threading is still not good --- TriviaBot/bot/ClientConnection.cpp | 5 +- TriviaBot/bot/ClientConnection.hpp | 4 +- TriviaBot/bot/GatewayHandler.cpp | 45 ++++--- TriviaBot/bot/GatewayHandler.hpp | 4 +- TriviaBot/bot/TriviaGame.cpp | 190 ++++++++++++++--------------- TriviaBot/bot/TriviaGame.hpp | 1 - 6 files changed, 122 insertions(+), 127 deletions(-) diff --git a/TriviaBot/bot/ClientConnection.cpp b/TriviaBot/bot/ClientConnection.cpp index 2e36632..a0a3143 100644 --- a/TriviaBot/bot/ClientConnection.cpp +++ b/TriviaBot/bot/ClientConnection.cpp @@ -49,7 +49,7 @@ ClientConnection::ClientConnection() { websocketpp::lib::placeholders::_1 )); - gHandler = new GatewayHandler(); + gh = std::make_unique(); } // Open a connection to the URI provided @@ -111,8 +111,9 @@ void ClientConnection::on_message(websocketpp::connection_hdl hdl, message_ptr m return; } + // Pass the message to the gateway handler - gHandler->handle_data(message->get_payload(), c, hdl); + gh->handle_data(message->get_payload(), c, hdl); } void ClientConnection::on_close(websocketpp::connection_hdl) { diff --git a/TriviaBot/bot/ClientConnection.hpp b/TriviaBot/bot/ClientConnection.hpp index 8392189..546ae7f 100644 --- a/TriviaBot/bot/ClientConnection.hpp +++ b/TriviaBot/bot/ClientConnection.hpp @@ -14,7 +14,7 @@ typedef websocketpp::config::asio_tls_client::message_type::ptr message_ptr; typedef websocketpp::lib::shared_ptr context_ptr; typedef client::connection_ptr connection_ptr; -class GatewayHandler; +#include "GatewayHandler.hpp" class ClientConnection { public: @@ -33,7 +33,7 @@ public: private: client c; - GatewayHandler *gHandler; + std::unique_ptr gh; }; #endif \ No newline at end of file diff --git a/TriviaBot/bot/GatewayHandler.cpp b/TriviaBot/bot/GatewayHandler.cpp index 4c7193e..5c89a5f 100644 --- a/TriviaBot/bot/GatewayHandler.cpp +++ b/TriviaBot/bot/GatewayHandler.cpp @@ -31,23 +31,19 @@ void GatewayHandler::handle_data(std::string data, client &c, websocketpp::conne } } -void GatewayHandler::heartbeat(websocketpp::lib::error_code const & ec, client *c, websocketpp::connection_hdl *hdl) { - json heartbeat = { - { "op", 1 }, - { "d", last_seq } - }; +void GatewayHandler::heartbeat(client *c, websocketpp::connection_hdl hdl, int interval) { + while (true) { + boost::this_thread::sleep(boost::posix_time::milliseconds(interval)); - c->send(*hdl, heartbeat.dump(), websocketpp::frame::opcode::text); + json heartbeat = { + { "op", 1 }, + { "d", last_seq } + }; - c->set_timer(heartbeat_interval, websocketpp::lib::bind( - &GatewayHandler::heartbeat, - this, - websocketpp::lib::placeholders::_1, - c, - hdl - )); + c->send(hdl, heartbeat.dump(), websocketpp::frame::opcode::text); - c->get_alog().write(websocketpp::log::alevel::app, "Sent heartbeat. (seq: " + std::to_string(last_seq) + ")"); + c->get_alog().write(websocketpp::log::alevel::app, "Sent heartbeat. (seq: " + std::to_string(last_seq) + ")"); + } } void GatewayHandler::on_hello(json decoded, client &c, websocketpp::connection_hdl &hdl) { @@ -55,13 +51,7 @@ void GatewayHandler::on_hello(json decoded, client &c, websocketpp::connection_h c.get_alog().write(websocketpp::log::alevel::app, "Heartbeat interval: " + std::to_string(heartbeat_interval / 1000.0f) + " seconds"); - c.set_timer(heartbeat_interval, websocketpp::lib::bind( - &GatewayHandler::heartbeat, - this, - websocketpp::lib::placeholders::_1, - &c, - &hdl - )); + heartbeat_thread = std::make_unique(boost::bind(&GatewayHandler::heartbeat, this, &c, hdl, heartbeat_interval)); identify(c, hdl); } @@ -78,13 +68,22 @@ void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connectio } else if (event_name == "GUILD_CREATE") { std::string guild_id = data["id"]; - guilds[guild_id] = std::make_unique(data); + try { + guilds[guild_id] = std::make_unique(data); + } + catch (std::domain_error err) { + // this doesn't even work + c.get_alog().write(websocketpp::log::elevel::rerror, "Domain error"); + } + + + c.get_alog().write(websocketpp::log::alevel::app, "Loaded guild: " + guilds[guild_id]->name); for (json channel : data["channels"]) { std::string channel_id = channel["id"]; channel["guild_id"] = guild_id; // create channel obj, add to overall channel list - channels[channel_id] = std::make_unique(channel); + channels[channel_id] = std::make_shared(channel); // add ptr to said channel list to guild's channel list guilds[guild_id]->channels.push_back(std::shared_ptr(channels[channel_id])); } diff --git a/TriviaBot/bot/GatewayHandler.hpp b/TriviaBot/bot/GatewayHandler.hpp index 12a845d..4b60448 100644 --- a/TriviaBot/bot/GatewayHandler.hpp +++ b/TriviaBot/bot/GatewayHandler.hpp @@ -42,7 +42,7 @@ public: void handle_data(std::string data, client &c, websocketpp::connection_hdl &hdl); - void heartbeat(websocketpp::lib::error_code const & ec, client *c, websocketpp::connection_hdl *hdl); + void heartbeat(client *c, websocketpp::connection_hdl hdl, int interval); void on_hello(json decoded, client &c, websocketpp::connection_hdl &hdl); @@ -69,6 +69,8 @@ private: // std::map> games; + std::unique_ptr heartbeat_thread; + APIHelper *ah; }; diff --git a/TriviaBot/bot/TriviaGame.cpp b/TriviaBot/bot/TriviaGame.cpp index b882148..a8c5210 100644 --- a/TriviaBot/bot/TriviaGame.cpp +++ b/TriviaBot/bot/TriviaGame.cpp @@ -23,6 +23,8 @@ TriviaGame::TriviaGame(GatewayHandler *gh, APIHelper *ah, std::string channel_id } TriviaGame::~TriviaGame() { + current_thread.reset(); + if (scores.size() == 0) { ah->send_message(channel_id, ":red_circle: Game cancelled!"); return; @@ -164,7 +166,7 @@ TriviaGame::~TriviaGame() { } void TriviaGame::start() { - question(); + current_thread = std::make_unique(boost::bind(&TriviaGame::question, this)); } void TriviaGame::interrupt() { @@ -172,127 +174,118 @@ void TriviaGame::interrupt() { } void TriviaGame::question() { - sqlite3 *db; int rc; std::string sql; + while (questions_asked < total_questions) { + sqlite3 *db; int rc; std::string sql; - /// open db - rc = sqlite3_open("bot/db/trivia.db", &db); - if (rc) { - std::cerr << "Cant't open database: " << sqlite3_errmsg(db) << std::endl; - } + /// open db + rc = sqlite3_open("bot/db/trivia.db", &db); + if (rc) { + std::cerr << "Cant't open database: " << sqlite3_errmsg(db) << std::endl; + } - // prepare statement - sqlite3_stmt *stmt; - sql = "SELECT * FROM Questions ORDER BY RANDOM() LIMIT 1;"; - rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0); + // prepare statement + sqlite3_stmt *stmt; + sql = "SELECT * FROM Questions ORDER BY RANDOM() LIMIT 1;"; + rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0); - if (rc != SQLITE_OK) { - std::cerr << "SQL error." << std::endl; - } + if (rc != SQLITE_OK) { + std::cerr << "SQL error." << std::endl; + } - rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) { - // result received - std::string id = reinterpret_cast(sqlite3_column_text(stmt, 0)); // converts id to string for us - std::string category = reinterpret_cast(sqlite3_column_text(stmt, 1)); - std::string question = reinterpret_cast(sqlite3_column_text(stmt, 2)); - std::string answer = reinterpret_cast(sqlite3_column_text(stmt, 3)); + rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) { + // result received + std::string id = reinterpret_cast(sqlite3_column_text(stmt, 0)); // converts id to string for us + std::string category = reinterpret_cast(sqlite3_column_text(stmt, 1)); + std::string question = reinterpret_cast(sqlite3_column_text(stmt, 2)); + std::string answer = reinterpret_cast(sqlite3_column_text(stmt, 3)); + + current_question = "#" + id + " [" + category + "] **" + question + "**"; + boost::algorithm::to_lower(answer); + boost::split(current_answers, answer, boost::is_any_of("*")); + + } + else if (rc != SQLITE_DONE) { + sqlite3_finalize(stmt); + std::cerr << "SQLite error." << std::endl; + } - current_question = "#" + id + " [" + category + "] **" + question + "**"; - boost::algorithm::to_lower(answer); - boost::split(current_answers, answer, boost::is_any_of("*")); - - } else if (rc != SQLITE_DONE) { sqlite3_finalize(stmt); - std::cerr << "SQLite error." << std::endl; + sqlite3_close(db); + + questions_asked++; + ah->send_message(channel_id, ":question: **(" + std::to_string(questions_asked) + "/" + std::to_string(total_questions) + ")** " + current_question); + question_start = boost::posix_time::microsec_clock::universal_time(); + + give_hint(0, ""); } - - sqlite3_finalize(stmt); - sqlite3_close(db); - - questions_asked++; - ah->send_message(channel_id, ":question: **(" + std::to_string(questions_asked) + "/" + std::to_string(total_questions) + ")** " + current_question); - question_start = boost::posix_time::microsec_clock::universal_time(); - - current_thread = std::make_unique(boost::bind(&TriviaGame::give_hint, this, 0, "")); + gh->delete_game(channel_id); } void TriviaGame::give_hint(int hints_given, std::string hint) { - boost::this_thread::sleep(interval); + while (hints_given < 4) { + boost::this_thread::sleep(interval); - std::string answer = *current_answers.begin(); + std::string answer = *current_answers.begin(); - bool print = false; + bool print = false; - if (hints_given == 0) { - hint = answer; - // probably shouldn't use regex here - boost::regex regexp("[a-zA-Z0-9]+?"); - hint = boost::regex_replace(hint, regexp, std::string(1, hide_char)); + if (hints_given == 0) { + hint = answer; + // probably shouldn't use regex here + boost::regex regexp("[a-zA-Z0-9]+?"); + hint = boost::regex_replace(hint, regexp, std::string(1, hide_char)); - print = true; - } else { - std::stringstream hint_stream(hint); + print = true; + } else { + std::stringstream hint_stream(hint); - std::random_device rd; - std::mt19937 rng(rd()); + std::random_device rd; + std::mt19937 rng(rd()); - std::vector hint_words, answer_words; - boost::split(hint_words, hint, boost::is_any_of(" ")); - boost::split(answer_words, answer, boost::is_any_of(" ")); + std::vector hint_words, answer_words; + boost::split(hint_words, hint, boost::is_any_of(" ")); + boost::split(answer_words, answer, boost::is_any_of(" ")); - hint = ""; - for (unsigned int i = 0; i < hint_words.size(); i++) { - std::string word = hint_words[i]; + hint = ""; + for (unsigned int i = 0; i < hint_words.size(); i++) { + std::string word = hint_words[i]; - // count number of *s - int length = 0; - for (unsigned int i = 0; i < word.length(); i++) { - if (word[i] == hide_char) { - length++; - } - } - - if (length > 1) { - std::uniform_int_distribution uni(0, word.length() - 1); - - bool replaced = false; - while (!replaced) { - int replace_index = uni(rng); - if (word[replace_index] == hide_char) { - word[replace_index] = answer_words[i][replace_index]; - - print = true; - replaced = true; + // count number of *s + int length = 0; + for (unsigned int i = 0; i < word.length(); i++) { + if (word[i] == hide_char) { + length++; } } - } - hint += word + " "; + if (length > 1) { + std::uniform_int_distribution uni(0, word.length() - 1); + + bool replaced = false; + while (!replaced) { + int replace_index = uni(rng); + if (word[replace_index] == hide_char) { + word[replace_index] = answer_words[i][replace_index]; + + print = true; + replaced = true; + } + } + } + + hint += word + " "; + } + } + + hints_given++; // now equal to the amount of [hide_char]s that need to be present in each word + + if (print) { + ah->send_message(channel_id, ":small_orange_diamond: Hint: **`" + hint + "`**"); } } - - hints_given++; // now equal to the amount of [hide_char]s that need to be present in each word - - if (print) { - ah->send_message(channel_id, ":small_orange_diamond: Hint: **`" + hint + "`**"); - } - - if (hints_given < 4) { - current_thread = std::make_unique(boost::bind(&TriviaGame::give_hint, this, hints_given, hint)); - } else { - current_thread = std::make_unique(boost::bind(&TriviaGame::question_failed, this)); - } -} - -void TriviaGame::question_failed() { boost::this_thread::sleep(interval); ah->send_message(channel_id, ":exclamation: Question failed. Answer: ** `" + *current_answers.begin() + "` **"); - - if (questions_asked < total_questions) { - question(); - } else { - gh->delete_game(channel_id); - } } void TriviaGame::handle_answer(std::string answer, DiscordObjects::User sender) { @@ -312,7 +305,8 @@ void TriviaGame::handle_answer(std::string answer, DiscordObjects::User sender) update_average_time(sender.id, diff.total_milliseconds()); if (questions_asked < total_questions) { - question(); + interrupt(); current_thread.reset(); // don't know if required + current_thread = std::make_unique(boost::bind(&TriviaGame::question, this)); } else { gh->delete_game(channel_id); } diff --git a/TriviaBot/bot/TriviaGame.hpp b/TriviaBot/bot/TriviaGame.hpp index 526427d..fb393db 100644 --- a/TriviaBot/bot/TriviaGame.hpp +++ b/TriviaBot/bot/TriviaGame.hpp @@ -33,7 +33,6 @@ private: void question(); void give_hint(int hints_given, std::string hint); - void question_failed(); void increase_score(std::string user_id); void update_average_time(std::string user_id, int time);