Store client version and source information in the database.
This will have wesnoth now provide the source where it was downloaded from the wesnothd. wesnothd will now store that, as well as the client's version in the `game_player_info` table. Backport of #4446
This commit is contained in:
parent
0d611acaf8
commit
5dbc990781
12 changed files with 81 additions and 40 deletions
1
data/dist
Normal file
1
data/dist
Normal file
|
@ -0,0 +1 @@
|
|||
Default
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
#include "utils/functional.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
static lg::log_domain log_mp("mp/main");
|
||||
#define DBG_MP LOG_STREAM(debug, log_mp)
|
||||
#define ERR_MP LOG_STREAM(err, log_mp)
|
||||
|
@ -156,6 +158,23 @@ std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
|
|||
config cfg;
|
||||
config res;
|
||||
cfg["version"] = game_config::version;
|
||||
|
||||
// TODO: retest with below string values - dist doesn't exist
|
||||
std::string info;
|
||||
std::ifstream infofile("./data/dist");
|
||||
if(infofile.is_open()){
|
||||
infofile >> info;
|
||||
infofile.close();
|
||||
|
||||
if(info == "Default" || info == "Steam" || info == "SourceForge" || info == "Flatpak"
|
||||
|| info == "macOS App Store" || info == "Linux repository" || info == "iOS" || info == "Android") {
|
||||
cfg["client_source"] = info;
|
||||
} else {
|
||||
cfg["client_source"] = "dist file has wrong value!";
|
||||
}
|
||||
} else {
|
||||
cfg["client_source"] = "failed to read dist file!";
|
||||
}
|
||||
res.add_child("version", std::move(cfg));
|
||||
sock->send_data(res);
|
||||
}
|
||||
|
|
|
@ -470,10 +470,10 @@ void fuh::db_update_game_end(const std::string& uuid, int game_id, const std::st
|
|||
}
|
||||
}
|
||||
|
||||
void fuh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction){
|
||||
void fuh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source){
|
||||
try {
|
||||
prepared_statement<void>("insert into `" + db_game_player_info_table_ + "`(INSTANCE_UUID, GAME_ID, USER_ID, SIDE_NUMBER, IS_HOST, FACTION) values(?, ?, IFNULL((select user_id from `"+db_users_table_+"` where username = ?), -1), ?, ?, ?)",
|
||||
uuid, game_id, username, side_number, is_host, faction);
|
||||
prepared_statement<void>("insert into `" + db_game_player_info_table_ + "`(INSTANCE_UUID, GAME_ID, USER_ID, SIDE_NUMBER, IS_HOST, FACTION, CLIENT_VERSION, CLIENT_SOURCE) values(?, ?, IFNULL((select user_id from `"+db_users_table_+"` where username = ?), -1), ?, ?, ?, ?, ?)",
|
||||
uuid, game_id, username, side_number, is_host, faction, version, source);
|
||||
} catch (const sql_error& e) {
|
||||
ERR_UH << "Could not insert the game's player information on table `" + db_game_player_info_table_ + "`:" << e.message << std::endl;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class fuh : public user_handler {
|
|||
void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name);
|
||||
void db_update_game_start(const std::string& uuid, int game_id, const std::string& map_name, const std::string& era_name, int reload);
|
||||
void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location);
|
||||
void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction);
|
||||
void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source);
|
||||
void db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name);
|
||||
void db_set_oos_flag(const std::string& uuid, int game_id);
|
||||
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
#include "lexical_cast.hpp"
|
||||
|
||||
wesnothd::player::player(const std::string& n, simple_wml::node& cfg,
|
||||
bool registered, const std::string& version, const size_t max_messages,
|
||||
bool registered, const std::string& version, const std::string& source, const size_t max_messages,
|
||||
const size_t time_period,
|
||||
const bool moderator)
|
||||
: name_(n)
|
||||
, version_(version)
|
||||
, source_(source)
|
||||
, cfg_(cfg)
|
||||
, registered_(registered)
|
||||
, flood_start_(0)
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
OBSERVING
|
||||
};
|
||||
|
||||
player(const std::string& n, simple_wml::node& cfg, bool registered, const std::string& version,
|
||||
player(const std::string& n, simple_wml::node& cfg, bool registered, const std::string& version, const std::string& source,
|
||||
const size_t max_messages=4, const size_t time_period=10,
|
||||
const bool moderator=false);
|
||||
|
||||
|
@ -46,6 +46,7 @@ public:
|
|||
|
||||
const std::string& name() const { return name_; }
|
||||
const std::string& version() const { return version_; }
|
||||
const std::string& source() const { return source_; }
|
||||
const simple_wml::node* config_address() const { return &cfg_; }
|
||||
|
||||
bool is_message_flooding();
|
||||
|
@ -56,6 +57,7 @@ public:
|
|||
private:
|
||||
const std::string name_;
|
||||
std::string version_;
|
||||
std::string source_;
|
||||
simple_wml::node& cfg_;
|
||||
|
||||
bool registered_;
|
||||
|
|
|
@ -247,8 +247,8 @@ void suh::db_update_game_start(const std::string& uuid, int game_id, const std::
|
|||
void suh::db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location){
|
||||
std::cout << uuid << " - " << game_id << " - " << replay_location << std::endl;
|
||||
}
|
||||
void suh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction){
|
||||
std::cout << uuid << " - " << game_id << " - " << username << " - " << side_number << " - " << is_host << " - " << faction << std::endl;
|
||||
void suh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source){
|
||||
std::cout << uuid << " - " << game_id << " - " << username << " - " << side_number << " - " << is_host << " - " << faction << " - " << version << " - " << source << std::endl;
|
||||
}
|
||||
void suh::db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name){
|
||||
std::cout << uuid << " - " << game_id << " - " << modification_name << std::endl;
|
||||
|
|
|
@ -71,7 +71,7 @@ class suh : public user_handler {
|
|||
void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name);
|
||||
void db_update_game_start(const std::string& uuid, int game_id, const std::string& map_name, const std::string& era_name, int reload);
|
||||
void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location);
|
||||
void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction);
|
||||
void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source);
|
||||
void db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name);
|
||||
void db_set_oos_flag(const std::string& uuid, int game_id);
|
||||
|
||||
|
|
|
@ -615,6 +615,9 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
|
|||
const simple_wml::string_span& version_str_span = (*version)["version"];
|
||||
const std::string version_str(version_str_span.begin(), version_str_span.end());
|
||||
|
||||
const simple_wml::string_span& source_str_span = (*version)["client_source"];
|
||||
const std::string source_str(source_str_span.begin(), source_str_span.end());
|
||||
|
||||
// Check if it is an accepted version.
|
||||
auto accepted_it = std::find_if(accepted_versions_.begin(), accepted_versions_.end(),
|
||||
std::bind(&utils::wildcard_string_match, version_str, _1));
|
||||
|
@ -622,7 +625,7 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
|
|||
if(accepted_it != accepted_versions_.end()) {
|
||||
LOG_SERVER << client_address(socket) << "\tplayer joined using accepted version " << version_str
|
||||
<< ":\ttelling them to log in.\n";
|
||||
async_send_doc(socket, login_response_, std::bind(&server::login, this, _1, version_str));
|
||||
async_send_doc(socket, login_response_, std::bind(&server::login, this, _1, version_str, source_str));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -659,23 +662,23 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
|
|||
}
|
||||
}
|
||||
|
||||
void server::login(socket_ptr socket, std::string version)
|
||||
void server::login(socket_ptr socket, std::string version, std::string source)
|
||||
{
|
||||
async_receive_doc(socket, std::bind(&server::handle_login, this, _1, _2, version));
|
||||
async_receive_doc(socket, std::bind(&server::handle_login, this, _1, _2, version, source));
|
||||
}
|
||||
|
||||
void server::handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version)
|
||||
void server::handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version, std::string source)
|
||||
{
|
||||
if(const simple_wml::node* const login = doc->child("login")) {
|
||||
if(!is_login_allowed(socket, login, version)) {
|
||||
server::login(socket, version); // keep reading logins from client until we get a successful one
|
||||
if(!is_login_allowed(socket, login, version, source)) {
|
||||
server::login(socket, version, source); // keep reading logins from client until we get a successful one
|
||||
}
|
||||
} else {
|
||||
async_send_error(socket, "You must login first.", MP_MUST_LOGIN);
|
||||
}
|
||||
}
|
||||
|
||||
bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version)
|
||||
bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version, const std::string& source)
|
||||
{
|
||||
// Check if the username is valid (all alpha-numeric plus underscore and hyphen)
|
||||
std::string username = (*login)["username"].to_string();
|
||||
|
@ -712,7 +715,7 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
|
|||
// Check for password
|
||||
|
||||
bool registered;
|
||||
if(!authenticate(socket, username, (*login)["password"].to_string(), version, name_taken, registered))
|
||||
if(!authenticate(socket, username, (*login)["password"].to_string(), version, source, name_taken, registered))
|
||||
return true; // it's a failed login but we don't want to call server::login again
|
||||
// because send_password_request() will handle the next network write and read instead
|
||||
|
||||
|
@ -790,13 +793,14 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
|
|||
}
|
||||
}
|
||||
|
||||
async_send_doc(socket, join_lobby_response_, [this, username, registered, version](socket_ptr socket) {
|
||||
async_send_doc(socket, join_lobby_response_, [this, username, registered, version, source](socket_ptr socket) {
|
||||
simple_wml::node& player_cfg = games_and_users_list_.root().add_child("user");
|
||||
add_player(socket, wesnothd::player(
|
||||
username,
|
||||
player_cfg,
|
||||
registered,
|
||||
version,
|
||||
source,
|
||||
default_max_messages_,
|
||||
default_time_period_,
|
||||
user_handler_ && user_handler_->user_is_moderator(username)
|
||||
|
@ -849,7 +853,7 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
|
|||
}
|
||||
|
||||
bool server::authenticate(
|
||||
socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, bool name_taken, bool& registered)
|
||||
socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, const std::string& source, bool name_taken, bool& registered)
|
||||
{
|
||||
// Current login procedure for registered nicks is:
|
||||
// - Client asks to log in with a particular nick
|
||||
|
@ -879,13 +883,13 @@ bool server::authenticate(
|
|||
if(password.empty()) {
|
||||
if(!name_taken) {
|
||||
send_password_request(socket, "The nickname '" + username + "' is registered on this server.",
|
||||
username, version, MP_PASSWORD_REQUEST);
|
||||
username, version, source, MP_PASSWORD_REQUEST);
|
||||
} else {
|
||||
send_password_request(socket,
|
||||
"The nickname '" + username + "' is registered on this server."
|
||||
"\n\nWARNING: There is already a client using this username, "
|
||||
"logging in will cause that client to be kicked!",
|
||||
username, version, MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME, true
|
||||
username, version, source, MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME, true
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -895,7 +899,7 @@ bool server::authenticate(
|
|||
// A password (or hashed password) was provided, however
|
||||
// there is no seed
|
||||
if(seeds_[socket.get()].empty()) {
|
||||
send_password_request(socket, "Please try again.", username, version, MP_NO_SEED_ERROR);
|
||||
send_password_request(socket, "Please try again.", username, version, source, MP_NO_SEED_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -936,7 +940,7 @@ bool server::authenticate(
|
|||
"You have made too many failed login attempts.", MP_TOO_MANY_ATTEMPTS_ERROR);
|
||||
} else {
|
||||
send_password_request(socket,
|
||||
"The password you provided for the nickname '" + username + "' was incorrect.", username,version,
|
||||
"The password you provided for the nickname '" + username + "' was incorrect.", username,version,source,
|
||||
MP_INCORRECT_PASSWORD_ERROR);
|
||||
}
|
||||
|
||||
|
@ -962,6 +966,7 @@ void server::send_password_request(socket_ptr socket,
|
|||
const std::string& msg,
|
||||
const std::string& user,
|
||||
const std::string& version,
|
||||
const std::string& source,
|
||||
const char* error_code,
|
||||
bool force_confirmation)
|
||||
{
|
||||
|
@ -980,7 +985,7 @@ void server::send_password_request(socket_ptr socket,
|
|||
"cannot log in due to an error in the hashing algorithm. "
|
||||
"Logging into your forum account on https://forums.wesnoth.org "
|
||||
"may fix this problem.");
|
||||
login(socket, version);
|
||||
login(socket, version, source);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -998,7 +1003,7 @@ void server::send_password_request(socket_ptr socket,
|
|||
e.set_attr("error_code", error_code);
|
||||
}
|
||||
|
||||
async_send_doc(socket, doc, std::bind(&server::login, this, _1, version));
|
||||
async_send_doc(socket, doc, std::bind(&server::login, this, _1, version, source));
|
||||
}
|
||||
|
||||
void server::add_player(socket_ptr socket, const wesnothd::player& player)
|
||||
|
@ -1744,7 +1749,19 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
const simple_wml::node::child_list& sides = g.get_sides_list();
|
||||
for(unsigned side_index = 0; side_index < sides.size(); ++side_index) {
|
||||
const simple_wml::node& side = *sides[side_index];
|
||||
user_handler_->db_insert_game_player_info(uuid_, g.id(), side["player_id"].to_string(), side["side"].to_int(), side["is_host"].to_bool(), side["faction"].to_string());
|
||||
const auto player = player_connections_.get<name_t>().find(side["player_id"].to_string());
|
||||
std::string version;
|
||||
std::string source;
|
||||
|
||||
// if "Nobody" is chosen for a side, for example
|
||||
if(player == player_connections_.get<name_t>().end()){
|
||||
version = "";
|
||||
source = "";
|
||||
} else {
|
||||
version = player->info().version();
|
||||
source = player->info().source();
|
||||
}
|
||||
user_handler_->db_insert_game_player_info(uuid_, g.id(), side["player_id"].to_string(), side["side"].to_int(), side["is_host"].to_bool(), side["faction"].to_string(), version, source);
|
||||
}
|
||||
|
||||
const std::string mods = multiplayer["active_mods"].to_string();
|
||||
|
|
|
@ -42,14 +42,13 @@ private:
|
|||
void handle_version(socket_ptr socket);
|
||||
void read_version(socket_ptr socket, std::shared_ptr<simple_wml::document> doc);
|
||||
|
||||
void login(socket_ptr socket, std::string version);
|
||||
void handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version);
|
||||
bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version);
|
||||
bool authenticate(socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, bool name_taken, bool& registered);
|
||||
void login(socket_ptr socket, std::string version, std::string source);
|
||||
void handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version, std::string source);
|
||||
bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version, const std::string& source);
|
||||
bool authenticate(socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, const std::string& source, bool name_taken, bool& registered);
|
||||
void send_password_request(socket_ptr socket, const std::string& msg,
|
||||
const std::string& user, const std::string& version, const char* error_code = "", bool force_confirmation = false);
|
||||
const std::string& user, const std::string& version, const std::string& source, const char* error_code = "", bool force_confirmation = false);
|
||||
bool accepting_connections() const { return !graceful_restart; }
|
||||
|
||||
void add_player(socket_ptr socket, const wesnothd::player&);
|
||||
void read_from_player(socket_ptr socket);
|
||||
void handle_read_from_player(socket_ptr socket, std::shared_ptr<simple_wml::document> doc);
|
||||
|
|
|
@ -175,7 +175,7 @@ class user_handler {
|
|||
virtual void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name) =0;
|
||||
virtual void db_update_game_start(const std::string& uuid, int game_id, const std::string& map_name, const std::string& era_name, int reload) =0;
|
||||
virtual void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location) =0;
|
||||
virtual void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction) =0;
|
||||
virtual void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source) =0;
|
||||
virtual void db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name) =0;
|
||||
virtual void db_set_oos_flag(const std::string& uuid, int game_id) =0;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ create table extra
|
|||
(
|
||||
USERNAME VARCHAR(100) NOT NULL,
|
||||
USER_LASTVISIT INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
USER_IS_MODERATOR BIT(1) NOT NULL DEFAULT 0,
|
||||
USER_IS_MODERATOR TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (USERNAME)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
@ -81,12 +81,14 @@ create table game_info
|
|||
-- STATUS: the status of the side, currently only updated at game end
|
||||
create table game_player_info
|
||||
(
|
||||
INSTANCE_UUID CHAR(36) NOT NULL,
|
||||
GAME_ID INT UNSIGNED NOT NULL,
|
||||
USER_ID INT NOT NULL,
|
||||
SIDE_NUMBER SMALLINT UNSIGNED NOT NULL,
|
||||
IS_HOST BIT(1) NOT NULL,
|
||||
FACTION VARCHAR(255) NOT NULL,
|
||||
INSTANCE_UUID CHAR(36) NOT NULL,
|
||||
GAME_ID INT UNSIGNED NOT NULL,
|
||||
USER_ID INT NOT NULL,
|
||||
SIDE_NUMBER SMALLINT UNSIGNED NOT NULL,
|
||||
IS_HOST BIT(1) NOT NULL,
|
||||
FACTION VARCHAR(255) NOT NULL,
|
||||
CLIENT_VERSION VARCHAR(255) NOT NULL DEFAULT '',
|
||||
CLIENT_SOURCE VARCHAR(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (INSTANCE_UUID, GAME_ID, SIDE_NUMBER)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue