Use an independent ID for the database's GAME_ID column.
Currently, wesnothd uses the game instance's `id_` value when inserting into the game_info, game_player_info, and game_modification_info tables. However, this value is unique only per game instance, not per scenario. This then results in problems when handling MP campaigns, which re-use the same game instance for multiple scenarios. Incrementing the game instance's `id_` was determined to not be suitable since it's used in a boost multi-index, and instead creating a brand new game instance for each scenario in an MP campaign is complex without being especially useful beyond this case.
This commit instead adds a separate `db_id_` value to each game instance, which can be incremented without causing any issues since it's used nowhere else, and is what will be inserted into the database. This means the `db_id_` is unique per scenario, and therefore resolves the below two issues.
Fixes #4281
Fixes #4341
Backport of 1a46bea027
This commit is contained in:
parent
0cc2bf4ca3
commit
54dd488d57
3 changed files with 29 additions and 6 deletions
|
@ -84,6 +84,7 @@ void send_to_players(simple_wml::document& data, const Container& players, socke
|
|||
}
|
||||
|
||||
int game::id_num = 1;
|
||||
int game::db_id_num = 1;
|
||||
|
||||
void game::missing_user(socket_ptr /*socket*/, const std::string& func) const
|
||||
{
|
||||
|
@ -98,6 +99,7 @@ game::game(player_connections& player_connections,
|
|||
const std::string& replay_save_path)
|
||||
: player_connections_(player_connections)
|
||||
, id_(id_num++)
|
||||
, db_id_(db_id_num++)
|
||||
, name_(name)
|
||||
, password_()
|
||||
, owner_(host)
|
||||
|
@ -1835,7 +1837,7 @@ static bool is_invalid_filename_char(char c)
|
|||
std::string game::get_replay_filename()
|
||||
{
|
||||
std::stringstream name;
|
||||
name << (*starting_pos(level_.root()))["name"] << " Turn " << current_turn() << " (" << id_ << ").bz2";
|
||||
name << (*starting_pos(level_.root()))["name"] << " Turn " << current_turn() << " (" << db_id_ << ").bz2";
|
||||
std::string filename(name.str());
|
||||
std::replace(filename.begin(), filename.end(), ' ', '_');
|
||||
filename.erase(std::remove_if(filename.begin(), filename.end(), is_invalid_filename_char), filename.end());
|
||||
|
|
|
@ -56,6 +56,16 @@ public:
|
|||
return id_;
|
||||
}
|
||||
|
||||
int db_id() const
|
||||
{
|
||||
return db_id_;
|
||||
}
|
||||
|
||||
void next_db_id()
|
||||
{
|
||||
db_id_ = db_id_num++;
|
||||
}
|
||||
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
|
@ -458,9 +468,16 @@ private:
|
|||
|
||||
player_connections& player_connections_;
|
||||
|
||||
// used for unique identification of game instances within wesnothd
|
||||
static int id_num;
|
||||
int id_;
|
||||
|
||||
// used for unique identification of games played in the database
|
||||
// necessary since for MP campaigns multiple scenarios can be played within the same game instance
|
||||
// and we need a unique ID per scenario played, not per game instance
|
||||
static int db_id_num;
|
||||
int db_id_;
|
||||
|
||||
/** The name of the game. */
|
||||
std::string name_;
|
||||
std::string password_;
|
||||
|
|
|
@ -1428,7 +1428,7 @@ void server::cleanup_game(game* game_ptr)
|
|||
metrics_.game_terminated(game_ptr->termination_reason());
|
||||
|
||||
if(user_handler_){
|
||||
user_handler_->db_update_game_end(uuid_, game_ptr->id(), game_ptr->get_replay_filename());
|
||||
user_handler_->db_update_game_end(uuid_, game_ptr->db_id(), game_ptr->get_replay_filename());
|
||||
}
|
||||
|
||||
simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
|
||||
|
@ -1671,6 +1671,9 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
}
|
||||
|
||||
g.save_replay();
|
||||
if(user_handler_){
|
||||
user_handler_->db_update_game_end(uuid_, g.db_id(), g.get_replay_filename());
|
||||
}
|
||||
|
||||
g.new_scenario(socket);
|
||||
g.reset_last_synced_context_id();
|
||||
|
@ -1678,6 +1681,7 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
// Record the full scenario in g.level()
|
||||
g.level().clear();
|
||||
scenario->copy_into(g.level().root());
|
||||
g.next_db_id();
|
||||
|
||||
if(g.description() == nullptr) {
|
||||
ERR_SERVER << client_address(socket) << "\tERROR: \"" << g.name() << "\" (" << g.id()
|
||||
|
@ -1748,7 +1752,7 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
// 1.14.9 and earlier also use whether observers are allowed to determine if the replay should be public
|
||||
// 1.14.10+ have a separate attribute for that
|
||||
bool is_public = m["private_replay"].to_string() == "" ? m["observer"].to_bool() : !m["private_replay"].to_bool();
|
||||
user_handler_->db_insert_game_info(uuid_, g.id(), game_config::wesnoth_version.str(), g.name(), m["mp_scenario"].to_string(), m["mp_era"].to_string(), g.is_reload(), m["observer"].to_bool(), is_public, g.has_password());
|
||||
user_handler_->db_insert_game_info(uuid_, g.db_id(), game_config::wesnoth_version.str(), g.name(), m["mp_scenario"].to_string(), m["mp_era"].to_string(), g.is_reload(), m["observer"].to_bool(), is_public, g.has_password());
|
||||
|
||||
const simple_wml::node::child_list& sides = g.get_sides_list();
|
||||
for(unsigned side_index = 0; side_index < sides.size(); ++side_index) {
|
||||
|
@ -1769,13 +1773,13 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
source = "Default";
|
||||
}
|
||||
}
|
||||
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, side["current_player"].to_string());
|
||||
user_handler_->db_insert_game_player_info(uuid_, g.db_id(), side["player_id"].to_string(), side["side"].to_int(), side["is_host"].to_bool(), side["faction"].to_string(), version, source, side["current_player"].to_string());
|
||||
}
|
||||
|
||||
const std::string mods = m["active_mods"].to_string();
|
||||
if(mods != "") {
|
||||
for(const std::string mod : utils::split(mods, ',')){
|
||||
user_handler_->db_insert_modification_info(uuid_, g.id(), mod);
|
||||
user_handler_->db_insert_modification_info(uuid_, g.db_id(), mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1901,7 +1905,7 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
|
|||
if((*info)["condition"].to_string() == "out of sync") {
|
||||
g.send_server_message_to_all(player.name() + " reports out of sync errors.");
|
||||
if(user_handler_){
|
||||
user_handler_->db_set_oos_flag(uuid_, g.id());
|
||||
user_handler_->db_set_oos_flag(uuid_, g.db_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue