diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 0520023a..446c8eb8 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -3335,8 +3335,9 @@ struct C_SetTeamFlag_BB_0FEA { parray flag_data; } __packed__; -// 10EA: Delete team result -// No arguments except header.flag +// 10EA: Delete team (C->S) and result (S->C) +// No arguments (C->S) +// No arguments except header.flag (S->C) // 11EA: Change team member privilege level // The format below is used only when the client sends this command; when the @@ -3371,16 +3372,17 @@ struct S_TeamMembershipInformation_BB_12EA { struct S_TeamInfoForPlayer_BB_13EA_15EA_Entry { // The client uses the first four of these to determine if the player is in a // team or not - if they are all zero, the player is not in a team. - le_uint32_t guild_card_number = 0; - le_uint32_t team_id = 0; - le_uint32_t unknown_a3 = 0; - le_uint32_t unknown_a4 = 0; - le_uint32_t privilege_level = 0; - pstring team_name; - le_uint32_t guild_card_number2 = 0; - le_uint32_t lobby_client_id = 0; - pstring player_name; - parray flag_data; + /* 0000 */ le_uint32_t guild_card_number = 0; + /* 0004 */ le_uint32_t team_id = 0; + /* 0008 */ le_uint32_t unknown_a3 = 0; + /* 000C */ le_uint32_t unknown_a4 = 0; + /* 0010 */ le_uint32_t privilege_level = 0; + /* 0014 */ pstring team_name; + /* 0034 */ le_uint32_t guild_card_number2 = 0; + /* 0038 */ le_uint32_t lobby_client_id = 0; + /* 003C */ pstring player_name; + /* 005C */ parray flag_data; + /* 085C */ } __packed__; // 14EA (C->S): Get team info for lobby players @@ -3455,16 +3457,15 @@ struct S_CrossTeamRanking_BB_1CEA { // 1DEA (S->C): Update team rewards bitmask // header.flag specifies the new rewards bitmask. -// 1EEA (C->S): Unknown +// 1EEA (C->S): Rename team // header.flag is used, but it's unknown what the value means. -struct C_Unknown_BB_1EEA { - pstring unknown_a1; +struct C_RenameTeam_BB_1EEA { + pstring new_team_name; } __packed__; -// 1FEA (S->C): Action result -// This command behaves exactly like 02EA. This command is presumably the -// response to whatever 1EEA does. +// 1FEA (S->C): Rename team result +// This command behaves like 02EA, but is sent in response to 1EEA instead. // 20EA: Unknown // header.flag is used, but no other arguments. When sent by the server, diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index b1b4690a..0be9e0a9 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4888,6 +4888,29 @@ static void on_EA_BB(shared_ptr c, uint16_t command, uint32_t flag, stri case 0x1CEA: send_cross_team_ranking(c); break; + case 0x1EEA: { + const auto& cmd = check_size_t(data); + auto team = c->team(); + string new_team_name = cmd.new_team_name.decode(c->language()); + if (!team) { + // TODO: What's the right error code to use here? + send_command(c, 0x1FEA, 0x00000001); + } else if (s->team_index->get_by_name(new_team_name)) { + send_command(c, 0x1FEA, 0x00000002); + } else { + s->team_index->rename(team->team_id, new_team_name); + send_command(c, 0x1FEA, 0x00000000); + for (const auto& it : team->members) { + try { + auto member_c = s->find_client(nullptr, it.second.serial_number); + send_update_team_metadata_for_client(c); + send_team_membership_info(c); + } catch (const out_of_range&) { + } + } + } + break; + } default: throw runtime_error("invalid team command"); } diff --git a/src/TeamIndex.cc b/src/TeamIndex.cc index 158a462f..5dffab7c 100644 --- a/src/TeamIndex.cc +++ b/src/TeamIndex.cc @@ -292,7 +292,7 @@ vector> TeamIndex::all() const { return ret; } -shared_ptr TeamIndex::create(string& name, uint32_t master_serial_number, const string& master_name) { +shared_ptr TeamIndex::create(const string& name, uint32_t master_serial_number, const string& master_name) { auto team = make_shared(this->next_team_id++); save_file(this->directory + "/base.json", JSON::dict({{"NextTeamID", this->next_team_id}}).serialize()); @@ -316,6 +316,16 @@ void TeamIndex::disband(uint32_t team_id) { team->delete_files(); } +void TeamIndex::rename(uint32_t team_id, const std::string& new_team_name) { + auto team = this->id_to_team.at(team_id); + if (!this->name_to_team.emplace(new_team_name, team).second) { + throw runtime_error("team name is already in use"); + } + this->name_to_team.erase(team->name); + team->name = new_team_name; + team->save_config(); +} + void TeamIndex::add_member(uint32_t team_id, uint32_t serial_number, const string& name) { auto team = this->id_to_team.at(team_id); if (!this->serial_number_to_team.emplace(serial_number, team).second) { diff --git a/src/TeamIndex.hh b/src/TeamIndex.hh index 2b01be75..852717be 100644 --- a/src/TeamIndex.hh +++ b/src/TeamIndex.hh @@ -127,8 +127,9 @@ public: std::shared_ptr get_by_serial_number(uint32_t serial_number) const; std::vector> all() const; - std::shared_ptr create(std::string& name, uint32_t master_serial_number, const std::string& master_name); + std::shared_ptr create(const std::string& name, uint32_t master_serial_number, const std::string& master_name); void disband(uint32_t team_id); + void rename(uint32_t team_id, const std::string& new_name); void add_member(uint32_t team_id, uint32_t serial_number, const std::string& name); void remove_member(uint32_t serial_number);