Fixup, complete, and enable the lobby's game history viewer.

At some point in the future I'd like to come back and add some search parameters - right now it shows the game history of the currently selected player in the lobby, but ideally it would instead have options for player name, era, scenario, etc.
This commit is contained in:
Pentarctagon 2023-02-23 21:46:05 -06:00 committed by Pentarctagon
parent c6b9ceb61e
commit 6fe2627048
12 changed files with 710 additions and 73 deletions

View file

@ -33,6 +33,7 @@ test_gui2/modal_dialog_test_hotkey_bind
test_gui2/modal_dialog_test_install_dependencies
test_gui2/modal_dialog_test_language_selection
test_gui2/modal_dialog_test_mp_lobby
test_gui2/modal_dialog_test_mp_match_history_dialog
test_gui2/modal_dialog_test_mp_report
test_gui2/modal_dialog_test_lobby_player_info
test_gui2/modal_dialog_test_log_settings

View file

@ -1,11 +1,42 @@
#textdomain wesnoth-lib
#define _GUI_HORIZONTAL_TAB ID
[row]
[column]
horizontal_grow = true
[grid]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "{ID}_game_name"
definition = "default"
[/label]
[/column]
[/row]
[/grid]
[/column]
[/row]
#enddef
#define _GUI_MATCH_HISTORY_LIST
{GUI_FORCE_WIDGET_MINIMUM_SIZE 600 400 (
[listbox]
id = "history"
definition = "default"
vertical_scrollbar_mode = "auto"
horizontal_scrollbar_mode = "never"
[list_definition]
@ -17,14 +48,193 @@
[toggle_panel]
definition = "default"
[grid]
[row]
[column]
horizontal_grow = true
[grid]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "game_name"
definition = "gold_large"
[/label]
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
[column]
[spacer]
[/spacer]
grow_factor = 1
horizontal_grow = true
vertical_alignment = top
[grid]
[row]
[column]
grow_factor = 1
horizontal_grow = true
vertical_alignment = top
[grid]
linked_group = "data_columns"
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "scenario_name"
definition = "default_small"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "game_start"
definition = "default_small"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[button]
id = "replay_download"
definition = "default_small"
label = _ "Download"
[/button]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "players"
definition = "default"
label = _ "None"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "modifications"
definition = "default"
label = _ "None"
[/label]
[/column]
[/row]
[/grid]
[/column]
[column]
grow_factor = 1
horizontal_grow = true
vertical_alignment = top
[grid]
linked_group = "data_columns"
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "era_name"
definition = "default_small"
use_markup = yes
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "version"
definition = "default_small"
[/label]
[/column]
[/row]
[/grid]
[/column]
[/row]
[/grid]
[/column]
[/row]
@ -40,6 +250,7 @@
[/list_definition]
[/listbox]
)}
#enddef
[window]
@ -64,10 +275,20 @@
id = "tooltip"
[/helptip]
[linked_group]
id = "tabs"
fixed_width = true
fixed_height = true
[/linked_group]
[linked_group]
id = "data_columns"
fixed_width = true
[/linked_group]
[grid]
[row]
grow_factor = 0
[column]
horizontal_grow = true
@ -77,6 +298,7 @@
[label]
id = "title"
definition = "title"
label = _ "Match History"
[/label]
[/column]
@ -84,11 +306,120 @@
[/row]
[row]
grow_factor = 1
[column]
grow_factor = 1
border = all
border_size = 5
[horizontal_listbox]
id = "tab_bar"
horizontal_scrollbar_mode = "never"
vertical_scrollbar_mode = "never"
[list_definition]
[row]
[column]
[toggle_panel]
linked_group = "tabs"
[grid]
[row]
[column]
border = all
border_size = 5
[spacer][/spacer]
[/column]
[column]
grow_factor = 1
border = all
border_size = 5
[label]
id = "tab_label"
wrap = true
[/label]
[/column]
[column]
border = all
border_size = 5
[spacer][/spacer]
[/column]
[/row]
[/grid]
[/toggle_panel]
[/column]
[/row]
[/list_definition]
[list_data]
[row]
[column]
[widget]
id = "tab_label"
label = _ "Info"
[/widget]
[/column]
[/row]
[row]
[column]
[widget]
id = "tab_label"
label = _ "Players"
[/widget]
[/column]
[/row]
[row]
[column]
[widget]
id = "tab_label"
label = _ "Modifications"
[/widget]
[/column]
[/row]
[/list_data]
[/horizontal_listbox]
[/column]
[/row]
[row]
[column]
horizontal_grow = true
border = "all"
border_size = 5
{_GUI_MATCH_HISTORY_LIST}
@ -97,10 +428,48 @@
[/row]
[row]
grow_factor = 0
[column]
grow_factor = 1
[grid]
[row]
[column]
horizontal_alignment = right
border = "all"
border_size = 5
[button]
id = "newer_history"
definition = "left_arrow_short_ornate"
[/button]
[/column]
[column]
horizontal_alignment = right
border = "all"
border_size = 5
[button]
id = "older_history"
definition = "right_arrow_short_ornate"
[/button]
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
[column]
horizontal_alignment = right
border = "all"
border_size = 5
@ -122,3 +491,4 @@
[/window]
#undef _GUI_MATCH_HISTORY_LIST
#undef _GUI_HORIZONTAL_TAB

View file

@ -149,6 +149,7 @@
4685124C24AE1A0B005B6EB1 /* game_config_view.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685124824AE1535005B6EB1 /* game_config_view.cpp */; };
4685124D24AE1A15005B6EB1 /* game_config_view.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685124824AE1535005B6EB1 /* game_config_view.cpp */; };
4685A5FB25B4501E006FD3A1 /* match_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685A5F925B4501E006FD3A1 /* match_history.cpp */; };
900000000000000000000009 /* match_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685A5F925B4501E006FD3A1 /* match_history.cpp */; };
4685A60225B45064006FD3A1 /* game_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685A60025B45064006FD3A1 /* game_history.cpp */; };
4685A60325B45064006FD3A1 /* game_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4685A60025B45064006FD3A1 /* game_history.cpp */; };
468A5B93258CD3B5004A80EF /* libboost_chrono-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 468A5B85258CD3B4004A80EF /* libboost_chrono-mt.dylib */; };
@ -6153,6 +6154,7 @@
1234567890ABCDEF12345679 /* file_progress.cpp in Sources */,
000000000000000000000007 /* achievements.cpp in Sources */,
000000000000000000000008 /* achievements_dialog.cpp in Sources */,
900000000000000000000009 /* match_history.cpp in Sources */,
91E3570B1CACC9B200774252 /* singleplayer.cpp in Sources */,
4649B88B20288EEF00827CFB /* surface.cpp in Sources */,
46F92DB02174F6A300602C1C /* campaign_selection.cpp in Sources */,

View file

@ -38,6 +38,7 @@
#include "gui/widgets/toggle_panel.hpp"
#include "gui/widgets/stacked_widget.hpp"
#include "gui/dialogs/server_info_dialog.hpp"
#include "gui/dialogs/multiplayer/match_history.hpp"
#include "addon/client.hpp"
#include "addon/manager_ui.hpp"
@ -654,8 +655,8 @@ void mp_lobby::pre_show(window& window)
auto& profile_button = find_widget<button>(profile_panel, "view_profile", false);
connect_signal_mouse_left_click(profile_button, std::bind(&mp_lobby::open_profile_url, this));
// TODO: implement
find_widget<button>(profile_panel, "view_match_history", false).set_active(false);
auto& history_button = find_widget<button>(profile_panel, "view_match_history", false);
connect_signal_mouse_left_click(history_button, std::bind(&mp_lobby::open_match_history, this));
}
}
@ -705,6 +706,14 @@ void mp_lobby::post_show(window& /*window*/)
plugins_context_.reset();
}
void mp_lobby::open_match_history()
{
const mp::user_info* info = player_list_.get_selected_info();
if(info) {
mp_match_history::display(info->name, network_connection_);
}
}
void mp_lobby::network_handler()
{
try {

View file

@ -115,6 +115,8 @@ private:
void open_profile_url();
void open_match_history();
void tab_switch_callback();
void refresh_lobby();

View file

@ -14,32 +14,231 @@
#include "gui/dialogs/multiplayer/match_history.hpp"
#include "desktop/open.hpp"
#include "formula/string_utils.hpp"
#include "game_initialization/lobby_data.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/listbox.hpp"
#include "gui/widgets/window.hpp"
#include "wesnothd_connection.hpp"
using namespace std::chrono_literals;
static lg::log_domain log_network("network");
#define DBG_NW LOG_STREAM(debug, log_network)
#define ERR_NW LOG_STREAM(err, log_network)
// TODO: put close button outside of scrollbar area
namespace gui2::dialogs
{
REGISTER_DIALOG(mp_match_history)
mp_match_history::mp_match_history(mp::user_info& info, wesnothd_connection& connection)
mp_match_history::mp_match_history(const std::string& player_name, wesnothd_connection& connection, bool wait_for_response)
: modal_dialog(window_id())
, info_(info)
, player_name_(player_name)
, connection_(connection)
, offset_(0)
, wait_for_response_(wait_for_response)
{
register_label("title", true, VGETTEXT("Match History — $player", {{"player", info_.name}}));
register_label("title", true, VGETTEXT("Match History — $player", {{"player", player_name_}}));
}
void mp_match_history::pre_show(window& /*window*/)
void mp_match_history::pre_show(window& win)
{
request_history(0);
button& newer_history = find_widget<button>(&win, "newer_history", false);
button& older_history = find_widget<button>(&win, "older_history", false);
connect_signal_mouse_left_click(newer_history, std::bind(&mp_match_history::newer_history_offset, this));
connect_signal_mouse_left_click(older_history, std::bind(&mp_match_history::older_history_offset, this));
update_display();
}
void mp_match_history::request_history(int offset)
void mp_match_history::newer_history_offset()
{
connection_.send_data({ "game_history_request", config { "offset", offset } });
offset_ -= 10;
// display update failed, set the offset back to what it was before
if(!update_display()) {
offset_ += 10;
}
}
void mp_match_history::older_history_offset()
{
offset_ += 10;
// display update failed, set the offset back to what it was before
if(!update_display()) {
offset_ -= 10;
}
}
bool mp_match_history::update_display()
{
const config history = request_history(offset_);
// request failed, nothing to do
if(history.child_count("game_history_result") == 0) {
return false;
}
listbox* history_box = find_widget<listbox>(get_window(), "history", false, true);
history_box->clear();
listbox* tab_bar = find_widget<listbox>(get_window(), "tab_bar", false, true);
connect_signal_notify_modified(*tab_bar, std::bind(&mp_match_history::tab_switch_callback, this));
int i = 0;
for(const config& game : history.child_range("game_history_result")) {
widget_data row;
grid& history_grid = history_box->add_row(row);
dynamic_cast<label*>(history_grid.find("game_name", false))->set_label(game["game_name"].str());
dynamic_cast<label*>(history_grid.find("scenario_name", false))->set_label(game["scenario_name"].str());
dynamic_cast<label*>(history_grid.find("era_name", false))->set_label("<span color='#baac7d'>"+_("Era: ")+"</span>"+game["era_name"].str());
dynamic_cast<label*>(history_grid.find("game_start", false))->set_label(game["game_start"].str()+_(" UTC+0"));
dynamic_cast<label*>(history_grid.find("version", false))->set_label(game["version"].str());
button* replay_download = dynamic_cast<button*>(history_grid.find("replay_download", false));
std::string replay_url = game["replay_url"].str();
if(!replay_url.empty()) {
connect_signal_mouse_left_click(*replay_download, std::bind(&desktop::open_object, game["replay_url"].str()));
} else {
replay_download->set_active(false);
}
std::vector<std::string> player_list;
for(const config& player : game.child_range("player")) {
player_list.emplace_back(player["name"].str()+": "+player["faction"].str());
}
label* players = dynamic_cast<label*>(history_grid.find("players", false));
players->set_label(utils::join(player_list, "\n"));
players->set_visible(gui2::widget::visibility::invisible);
label* modifications = dynamic_cast<label*>(history_grid.find("modifications", false));
const auto& children = game.child_range("modification");
if(!children.empty()) {
std::vector<std::string> modifications_list;
for(const config& modification : game.child_range("modification")) {
modifications_list.emplace_back(modification["name"].str());
}
modifications->set_label(utils::join(modifications_list, "\n"));
}
modifications->set_visible(gui2::widget::visibility::invisible);
i++;
if(i == 10) {
break;
}
}
// this is already the most recent history, can't get anything newer
if(offset_ == 0) {
button* newer_history = find_widget<button>(get_window(), "newer_history", false, true);
newer_history->set_active(false);
} else {
button* newer_history = find_widget<button>(get_window(), "newer_history", false, true);
newer_history->set_active(true);
}
// the server returns up to 11 and the client displays at most 10
// if fewer than 11 rows are returned, then there are no older rows left to get next
if(history.child_count("game_history_result") < 11) {
button* older_history = find_widget<button>(get_window(), "older_history", false, true);
older_history->set_active(false);
} else {
button* older_history = find_widget<button>(get_window(), "older_history", false, true);
older_history->set_active(true);
}
return true;
}
const config mp_match_history::request_history(int offset)
{
config request;
config& child = request.add_child("game_history_request");
child["offset"] = offset;
child["search_for"] = player_name_;
DBG_NW << request.debug();
connection_.send_data(request);
int times_waited = 0;
while(true) {
config response;
// I'm not really sure why this works to be honest
// I would've expected that there would be a risk of regular lobby responses showing up here since it's a reference to the lobby's network connection
// however testing has resulted in showing that this is not the case
// lobby responses are received in the lobby's network_handler() method when this method is not running
// lobby responses are not received while this method is running, and are handled in the lobby after it completes
// history results are never received in the lobby
if(connection_.receive_data(response)) {
if(response.child_count("game_history_result") == 0) {
DBG_NW << "Received non-history data: " << response.debug();
if(!response["error"].str().empty()) {
DBG_NW << "Received error from server: " << response["error"].str();
return {};
}
} else {
DBG_NW << "Received history data: " << response.debug();
return response;
}
} else {
DBG_NW << "Received no data";
}
if(times_waited > 20 || !wait_for_response_) {
ERR_NW << "Timed out waiting for history data, returning nothing";
return {};
}
times_waited++;
std::this_thread::sleep_for(250ms);
}
DBG_NW << "Something else happened while waiting for history data, returning nothing";
return {};
}
void mp_match_history::tab_switch_callback()
{
listbox* history_box = find_widget<listbox>(get_window(), "history", false, true);
listbox* tab_bar = find_widget<listbox>(get_window(), "tab_bar", false, true);
int tab = tab_bar->get_selected_row();
for(int i = 0; i < 10; i++) {
grid* history_grid = history_box->get_row_grid(i);
if(tab == 0) {
dynamic_cast<label*>(history_grid->find("scenario_name", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<label*>(history_grid->find("era_name", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<label*>(history_grid->find("game_start", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<label*>(history_grid->find("version", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<button*>(history_grid->find("replay_download", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<label*>(history_grid->find("players", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("modifications", false))->set_visible(gui2::widget::visibility::invisible);
} else if(tab == 1) {
dynamic_cast<label*>(history_grid->find("scenario_name", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("era_name", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("game_start", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("version", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<button*>(history_grid->find("replay_download", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("players", false))->set_visible(gui2::widget::visibility::visible);
dynamic_cast<label*>(history_grid->find("modifications", false))->set_visible(gui2::widget::visibility::invisible);
} else if(tab == 2) {
dynamic_cast<label*>(history_grid->find("scenario_name", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("era_name", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("game_start", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("version", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<button*>(history_grid->find("replay_download", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("players", false))->set_visible(gui2::widget::visibility::invisible);
dynamic_cast<label*>(history_grid->find("modifications", false))->set_visible(gui2::widget::visibility::visible);
}
}
}
} // namespace dialogs

View file

@ -16,8 +16,6 @@
#include "gui/dialogs/modal_dialog.hpp"
namespace mp { struct user_info; }
class wesnothd_connection;
namespace gui2
@ -29,25 +27,70 @@ namespace dialogs
class mp_match_history : public modal_dialog
{
public:
mp_match_history(mp::user_info& info, wesnothd_connection& connection);
/**
* Creates a dialog to view a player's history 10 games at a time.
*
* @param player_name The username of the player whose history is being viewed
* @param connection A reference to the lobby's network connection to wesnothd
* @param wait_for_response Whether to wait a few seconds for a response or not.
*/
mp_match_history(const std::string& player_name, wesnothd_connection& connection, bool wait_for_response = true);
/**
* The display function.
*
* See @ref modal_dialog for more information.
*/
DEFINE_SIMPLE_DISPLAY_WRAPPER(mp_match_history)
static void display(const std::string& player_name, wesnothd_connection& connection, bool wait_for_response = true)
{
mp_match_history(player_name, connection, wait_for_response).show();
}
private:
virtual const std::string& window_id() const override;
virtual void pre_show(window& window) override;
void request_history(int offset);
/**
* Requests game history from the server based on the offset.
* 11 rows are returned and 10 displayed - the presence of the 11th indicates whether incrementing the offset again would return any data.
* A request can time out if the server takes too long to respond so that a failure by the server to respond at all doesn't lock the game indefinitely.
*
* @param offset
* @return A config containing the game history information or an empty config if either the request times out or returns with an error
*/
const config request_history(int offset);
mp::user_info& info_;
/**
* Updates the dialog with the information returned by the server.
* This is called on dialog open as well as when incrementing or decrementing the offset.
*
* @return Whether the game history information was returned by the server or not.
*/
bool update_display();
/**
* Handles changing the selected horizontal listbox item for the specified game history row.
*/
void tab_switch_callback();
/** Increments the offset to use for querying data by 10 and updates the information displayed by the dialog. */
void newer_history_offset();
/** Decrements the offset to use for querying data by 10 and updates the information displayed by the dialog. */
void older_history_offset();
/** The username of the player whose history is being viewed */
std::string player_name_;
/** A reference to the lobby's network connection to wesnothd */
wesnothd_connection& connection_;
/** The offset to start retrieving history data at - should be increments of 10 */
int offset_;
/**
* Whether to wait a few seconds for a response or not.
* True for the regular client.
* False for the boost unit tests, since otherwise the gui2 test times out waiting for the request to the dummy wesnothd_connection to fail.
*/
bool wait_for_response_;
};
} // namespace dialogs

View file

@ -123,20 +123,23 @@ std::unique_ptr<simple_wml::document> dbconn::get_game_history(int player_id, in
{
std::string game_history_query = "select "
" game.GAME_NAME, "
" game.RELOAD, "
" game.START_TIME, "
" GROUP_CONCAT(CONCAT(player.USER_NAME, ':', player.FACTION)) as PLAYERS, "
" IFNULL(scenario.NAME, '') as SCENARIO_NAME, "
" IFNULL(scenario.ID, '') as SCENARIO_ID, "
" IFNULL(era.NAME, '') as ERA_NAME, "
" IFNULL(era.ID, '') as ERA_ID, "
" IFNULL(GROUP_CONCAT(distinct mods.NAME, '') as MODIFICATION_NAMES, "
" IFNULL(GROUP_CONCAT(distinct mods.ID), '') as MODIFICATION_IDS, "
" IFNULL(GROUP_CONCAT(distinct mods.NAME), '') as MODIFICATION_NAMES, "
" case "
" when game.PUBLIC = 1 "
" when game.PUBLIC = 1 and game.INSTANCE_VERSION != 'trunk' "
" then concat('https://replays.wesnoth.org/', substring(game.INSTANCE_VERSION, 1, 4), '/', year(game.END_TIME), '/', lpad(month(game.END_TIME), 2, '0'), '/', lpad(day(game.END_TIME), 2, '0'), '/', game.REPLAY_NAME) "
" when game.PUBLIC = 1 and game.INSTANCE_VERSION = 'trunk' "
" then concat('https://replays.wesnoth.org/', game.INSTANCE_VERSION, '/', year(game.END_TIME), '/', lpad(month(game.END_TIME), 2, '0'), '/', lpad(day(game.END_TIME), 2, '0'), '/', game.REPLAY_NAME) "
" else '' "
" end as REPLAY_URL "
" end as REPLAY_URL, "
" case "
" when game.INSTANCE_VERSION != 'trunk' "
" then SUBSTRING(game.INSTANCE_VERSION, 1, 4) "
" else 'trunk' "
" end as VERSION "
"from "+db_game_info_table_+" game "
"inner join "+db_game_player_info_table_+" player "
" on exists "

View file

@ -27,12 +27,9 @@ void game_history::read(mariadb::result_set_ref rslt)
{
result r;
r.game_name = rslt->get_string("GAME_NAME");
r.reload = rslt->get_boolean("RELOAD");
r.game_start = rslt->get_date_time("START_TIME").str();
r.scenario_name = rslt->get_string("SCENARIO_NAME");
r.scenario_id = rslt->get_string("SCENARIO_ID");
r.era_name = rslt->get_string("ERA_NAME");
r.era_id = rslt->get_string("ERA_ID");
for(const auto& player_info : utils::split(rslt->get_string("PLAYERS")))
{
std::vector<std::string> info = utils::split(player_info, ':');
@ -46,8 +43,8 @@ void game_history::read(mariadb::result_set_ref rslt)
}
}
r.modification_names = utils::split(rslt->get_string("MODIFICATION_NAMES"));
r.modification_ids = utils::split(rslt->get_string("MODIFICATION_IDS"));
r.replay_url = rslt->get_string("REPLAY_URL");
r.version = rslt->get_string("VERSION");
results.push_back(std::move(r));
}
}
@ -59,12 +56,11 @@ std::unique_ptr<simple_wml::document> game_history::to_doc()
{
simple_wml::node& ghr = doc->root().add_child("game_history_result");
ghr.set_attr_dup("game_name", result.game_name.c_str());
ghr.set_attr_int("reload", result.reload);
ghr.set_attr_dup("game_start", result.game_start.c_str());
ghr.set_attr_dup("scenario_name", result.scenario_name.c_str());
ghr.set_attr_dup("scenario_id", result.scenario_id.c_str());
ghr.set_attr_dup("era_name", result.era_name.c_str());
ghr.set_attr_dup("era_id", result.era_id.c_str());
ghr.set_attr_dup("replay_url", result.replay_url.c_str());
ghr.set_attr_dup("version", result.version.c_str());
for(const auto& player : result.players)
{
simple_wml::node& p = ghr.add_child("player");
@ -76,11 +72,6 @@ std::unique_ptr<simple_wml::document> game_history::to_doc()
simple_wml::node& m = ghr.add_child("modification");
m.set_attr_dup("name", mod.c_str());
}
for(const auto& mod : result.modification_ids)
{
simple_wml::node& m = ghr.add_child("modification");
m.set_attr_dup("id", mod.c_str());
}
}
return doc;
}

View file

@ -32,16 +32,13 @@ class game_history : public rs_base
struct result
{
std::string game_name;
int reload;
std::string game_start;
std::string scenario_name;
std::string scenario_id;
std::string era_name;
std::string era_id;
std::vector<player> players;
std::vector<std::string> modification_names;
std::vector<std::string> modification_ids;
std::string replay_url;
std::string version;
};
public:

View file

@ -1139,6 +1139,34 @@ void server::handle_player_in_lobby(player_iterator player, simple_wml::document
handle_join_game(player, *join);
return;
}
if(simple_wml::node* request = data.child("game_history_request")) {
if(user_handler_) {
int offset = request->attr("offset").to_int();
int player_id = 0;
// if no search_for attribute -> get the requestor's forum id
// if search_for attribute for offline player -> query the forum database for the forum id
// if search_for attribute for online player -> get the forum id from wesnothd's player info
if(!request->has_attr("search_for")) {
player_id = player->info().config_address()->attr("forum_id").to_int();
} else {
std::string player_name = request->attr("search_for").to_string();
auto player_ptr = player_connections_.get<name_t>().find(player_name);
if(player_ptr == player_connections_.get<name_t>().end()) {
player_id = user_handler_->get_forum_id(player_name);
} else {
player_id = player_ptr->info().config_address()->attr("forum_id").to_int();
}
}
if(player_id != 0) {
LOG_SERVER << "Querying game history requested by player `" << player->info().name() << "` for player id `" << player_id << "`.";
user_handler_->async_get_and_send_game_history(io_service_, *this, player, player_id, offset);
}
}
return;
}
}
void server::handle_whisper(player_iterator player, simple_wml::node& whisper)
@ -1867,32 +1895,6 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data
} else if(data.child("stop_updates")) {
g.send_data(data, p);
return;
} else if(simple_wml::node* request = data.child("game_history_request")) {
if(user_handler_) {
int offset = request->attr("offset").to_int();
int player_id = 0;
// if no search_for attribute -> get the requestor's forum id
// if search_for attribute for offline player -> query the forum database for the forum id
// if search_for attribute for online player -> get the forum id from wesnothd's player info
if(!request->has_attr("search_for")) {
player_id = player.config_address()->attr("forum_id").to_int();
} else {
std::string player_name = request->attr("search_for").to_string();
auto player_ptr = player_connections_.get<name_t>().find(player_name);
if(player_ptr == player_connections_.get<name_t>().end()) {
player_id = user_handler_->get_forum_id(player_name);
} else {
player_id = player_ptr->info().config_address()->attr("forum_id").to_int();
}
}
if(player_id != 0) {
LOG_SERVER << "Querying game history requested by player `" << player.name() << "` for player id `" << player_id << "`.";
user_handler_->async_get_and_send_game_history(io_service_, *this, p, player_id, offset);
}
}
return;
// Data to ignore.
} else if(
data.child("error") ||

View file

@ -85,6 +85,7 @@
#include "gui/dialogs/multiplayer/mp_join_game.hpp"
#include "gui/dialogs/multiplayer/mp_join_game_password_prompt.hpp"
#include "gui/dialogs/multiplayer/mp_login.hpp"
#include "gui/dialogs/multiplayer/match_history.hpp"
#include "gui/dialogs/multiplayer/mp_method_selection.hpp"
#include "gui/dialogs/multiplayer/mp_report.hpp"
#include "gui/dialogs/multiplayer/mp_staging.hpp"
@ -600,6 +601,10 @@ BOOST_AUTO_TEST_CASE(modal_dialog_test_achievements_dialog)
{
test<achievements_dialog>();
}
BOOST_AUTO_TEST_CASE(modal_dialog_test_mp_match_history_dialog)
{
test<mp_match_history>();
}
BOOST_AUTO_TEST_CASE(modeless_dialog_test_debug_clock)
{
test_popup<debug_clock>();
@ -983,6 +988,19 @@ struct dialog_tester<mp_lobby>
}
};
template<>
struct dialog_tester<mp_match_history>
{
wesnothd_connection connection;
dialog_tester() : connection("", "")
{
}
mp_match_history* create()
{
return new mp_match_history("", connection, false);
}
};
class fake_chat_handler : public events::chat_handler {
void add_chat_message(const std::time_t&,
const std::string&, int, const std::string&,