Also store where the client came from.
This commit is contained in:
parent
c04ecaa8fb
commit
21f9b19120
10 changed files with 71 additions and 31 deletions
1
data/dist
Normal file
1
data/dist
Normal file
|
@ -0,0 +1 @@
|
|||
Default
|
|
@ -44,6 +44,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)
|
||||
|
@ -147,6 +149,23 @@ std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
|
|||
config cfg;
|
||||
config res;
|
||||
cfg["version"] = game_config::wesnoth_version.str();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -445,10 +445,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, const std::string& version){
|
||||
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, CLIENT_VERSION) VALUES(?, ?, IFNULL((SELECT user_id FROM `"+db_users_table_+"` WHERE username = ?), -1), ?, ?, ?, ?)",
|
||||
uuid, game_id, username, side_number, is_host, faction, version);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,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, const std::string& version);
|
||||
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 std::size_t max_messages,
|
||||
bool registered, const std::string& version, const std::string& source, const std::size_t max_messages,
|
||||
const std::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 std::size_t max_messages=4, const std::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_;
|
||||
|
|
|
@ -590,6 +590,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));
|
||||
|
@ -597,7 +600,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;
|
||||
}
|
||||
|
||||
|
@ -634,23 +637,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();
|
||||
|
@ -687,7 +690,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
|
||||
|
||||
|
@ -765,13 +768,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)
|
||||
|
@ -822,7 +826,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
|
||||
|
@ -852,13 +856,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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -868,7 +872,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;
|
||||
}
|
||||
|
||||
|
@ -909,7 +913,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);
|
||||
}
|
||||
|
||||
|
@ -935,6 +939,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)
|
||||
{
|
||||
|
@ -953,7 +958,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;
|
||||
}
|
||||
|
||||
|
@ -971,7 +976,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)
|
||||
|
@ -1620,8 +1625,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];
|
||||
std::string version = player_connections_.get<name_t>().find(side["player_id"].to_string())->info().version();
|
||||
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);
|
||||
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,12 +42,12 @@ 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&);
|
||||
|
|
|
@ -138,7 +138,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, const std::string& version) =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;
|
||||
|
||||
|
@ -88,6 +88,7 @@ create table game_player_info
|
|||
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