fix team membership struct

This commit is contained in:
Martin Michelsen
2025-03-08 22:55:25 -08:00
parent c7d606247f
commit 5f838815ab
8 changed files with 109 additions and 86 deletions
+9 -14
View File
@@ -3044,8 +3044,9 @@ struct S_TournamentEntryList_Ep3_E2 {
// E2 (S->C): Set system file contents (BB) // E2 (S->C): Set system file contents (BB)
struct S_SyncSystemFile_BB_E2 { struct S_SyncSystemFile_BB_E2 {
PSOBBBaseSystemFile system_file; /* 0000 */ PSOBBBaseSystemFile system_file;
PSOBBTeamMembership team_membership; /* 02B8 */ PSOBBFullTeamMembership team_membership;
/* 0AF0 */
} __packed_ws__(S_SyncSystemFile_BB_E2, 0xAF0); } __packed_ws__(S_SyncSystemFile_BB_E2, 0xAF0);
// E3 (S->C): Game or tournament info (Episode 3) // E3 (S->C): Game or tournament info (Episode 3)
@@ -3244,7 +3245,7 @@ struct C_CreateSpectatorTeam_Ep3_E7 {
struct SC_SyncSaveFiles_BB_E7 { struct SC_SyncSaveFiles_BB_E7 {
/* 0000 */ PSOBBCharacterFile char_file; /* 0000 */ PSOBBCharacterFile char_file;
/* 2EA4 */ PSOBBBaseSystemFile system_file; /* 2EA4 */ PSOBBBaseSystemFile system_file;
/* 30DC */ PSOBBTeamMembership team_membership; /* 315C */ PSOBBFullTeamMembership team_membership;
/* 3994 */ /* 3994 */
} __packed_ws__(SC_SyncSaveFiles_BB_E7, 0x3994); } __packed_ws__(SC_SyncSaveFiles_BB_E7, 0x3994);
@@ -3488,19 +3489,13 @@ struct C_ChangeTeamMemberPrivilegeLevel_BB_11EA {
} __packed_ws__(C_ChangeTeamMemberPrivilegeLevel_BB_11EA, 4); } __packed_ws__(C_ChangeTeamMemberPrivilegeLevel_BB_11EA, 4);
// 12EA (S->C): Team membership information // 12EA (S->C): Team membership information
// If the client is not in a team, all fields should be zero. // This updates the client's view of its system file.
struct S_TeamMembershipInformation_BB_12EA { struct S_TeamMembershipInformation_BB_12EA {
le_uint32_t unknown_a1 = 0; // If skip_update_system_file is not zero, the client ignores the command.
le_uint32_t guild_card_number = 0; // It's not clear why this field exists.
le_uint32_t team_id = 0; le_uint32_t skip_update_system_file = 0;
le_uint32_t unknown_a4 = 0; PSOBBBaseTeamMembership membership;
le_uint32_t unknown_a6 = 0;
uint8_t privilege_level = 0;
uint8_t team_member_count = 0;
uint8_t unknown_a8 = 0;
uint8_t unknown_a9 = 0;
pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
} __packed_ws__(S_TeamMembershipInformation_BB_12EA, 0x38); } __packed_ws__(S_TeamMembershipInformation_BB_12EA, 0x38);
// 13EA: Team info for lobby players // 13EA: Team info for lobby players
+9 -42
View File
@@ -5237,9 +5237,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
c->login->account->save(); c->login->account->save();
send_command(c, 0x02EA, 0x00000000); send_command(c, 0x02EA, 0x00000000);
send_update_team_metadata_for_client(c); send_team_membership_change_notifications(c);
send_team_membership_info(c);
send_update_team_reward_flags(c);
} }
break; break;
} }
@@ -5274,8 +5272,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
added_c->login->account->account_id, added_c->login->account->account_id,
added_c->character()->disp.name.decode(added_c->language())); added_c->character()->disp.name.decode(added_c->language()));
send_update_team_metadata_for_client(added_c); send_team_membership_change_notifications(added_c);
send_team_membership_info(added_c);
send_command(c, 0x04EA, 0x00000000); send_command(c, 0x04EA, 0x00000000);
send_command(added_c, 0x04EA, 0x00000000); send_command(added_c, 0x04EA, 0x00000000);
} }
@@ -5306,8 +5303,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
} }
} }
if (removed_c) { if (removed_c) {
send_update_team_metadata_for_client(removed_c); send_team_membership_change_notifications(removed_c);
send_team_membership_info(removed_c);
} }
} else { } else {
// TODO: Figure out the right error code to use here. // TODO: Figure out the right error code to use here.
@@ -5352,13 +5348,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
if (team && team->members.at(c->login->account->account_id).check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) { if (team && team->members.at(c->login->account->account_id).check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
const auto& cmd = check_size_t<C_SetTeamFlag_BB_0FEA>(data); const auto& cmd = check_size_t<C_SetTeamFlag_BB_0FEA>(data);
s->team_index->set_flag_data(team->team_id, cmd.flag_data); s->team_index->set_flag_data(team->team_id, cmd.flag_data);
for (const auto& it : team->members) { send_team_metadata_change_notifications(s, team, TeamMetadataChange::FLAG_DATA);
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
send_update_team_metadata_for_client(member_c);
} catch (const out_of_range&) {
}
}
} }
break; break;
} }
@@ -5366,16 +5356,8 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
auto team = c->team(); auto team = c->team();
if (team && team->members.at(c->login->account->account_id).check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) { if (team && team->members.at(c->login->account->account_id).check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
s->team_index->disband(team->team_id); s->team_index->disband(team->team_id);
send_command(c, 0x10EA, 0x00000000); send_command(c, 0x10EA, 0x00000000);
for (const auto& it : team->members) { send_team_metadata_change_notifications(s, team, TeamMetadataChange::TEAM_DISBANDED);
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
send_update_team_metadata_for_client(member_c);
send_team_membership_info(member_c);
} catch (const out_of_range&) {
}
}
} }
break; break;
} }
@@ -5429,14 +5411,12 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
} }
} }
if (send_updates_for_this_m) { if (send_updates_for_this_m) {
send_update_team_metadata_for_client(c); send_team_membership_change_notifications(c);
send_team_membership_info(c);
} }
if (send_updates_for_other_m) { if (send_updates_for_other_m) {
try { try {
auto other_c = s->find_client(nullptr, cmd.guild_card_number); auto other_c = s->find_client(nullptr, cmd.guild_card_number);
send_update_team_metadata_for_client(other_c); send_team_membership_change_notifications(other_c);
send_team_membership_info(other_c);
} catch (const out_of_range&) { } catch (const out_of_range&) {
} }
} }
@@ -5474,13 +5454,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
s->team_index->buy_reward(team->team_id, reward.key, reward.team_points, reward.reward_flag); s->team_index->buy_reward(team->team_id, reward.key, reward.team_points, reward.reward_flag);
if (reward.reward_flag != TeamIndex::Team::RewardFlag::NONE) { if (reward.reward_flag != TeamIndex::Team::RewardFlag::NONE) {
for (const auto& it : team->members) { send_team_metadata_change_notifications(s, team, TeamMetadataChange::REWARD_FLAGS);
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
send_update_team_reward_flags(member_c);
} catch (const out_of_range&) {
}
}
} }
if (!reward.reward_item.empty()) { if (!reward.reward_item.empty()) {
c->current_bank().add_item(reward.reward_item, *s->item_stack_limits(c->version())); c->current_bank().add_item(reward.reward_item, *s->item_stack_limits(c->version()));
@@ -5503,14 +5477,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
} else { } else {
s->team_index->rename(team->team_id, new_team_name); s->team_index->rename(team->team_id, new_team_name);
send_command(c, 0x1FEA, 0x00000000); send_command(c, 0x1FEA, 0x00000000);
for (const auto& it : team->members) { send_team_metadata_change_notifications(s, team, TeamMetadataChange::TEAM_NAME);
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
send_update_team_metadata_for_client(c);
send_team_membership_info(c);
} catch (const out_of_range&) {
}
}
} }
break; break;
} }
+3 -3
View File
@@ -1173,7 +1173,7 @@ PSOCHARFile::LoadSharedResult PSOCHARFile::load_shared(const string& filename, b
if (header.flag != 0x00000000) { if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header"); throw runtime_error("incorrect flag in character file header");
} }
static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership) == 0x3994, ".psochar size is incorrect"); static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBFullTeamMembership) == 0x3994, ".psochar size is incorrect");
LoadSharedResult ret; LoadSharedResult ret;
ret.character_file = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get())); ret.character_file = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
@@ -1188,7 +1188,7 @@ void PSOCHARFile::save(
std::shared_ptr<const PSOBBBaseSystemFile> system, std::shared_ptr<const PSOBBBaseSystemFile> system,
std::shared_ptr<const PSOBBCharacterFile> character) { std::shared_ptr<const PSOBBCharacterFile> character) {
auto f = phosg::fopen_unique(filename, "wb"); auto f = phosg::fopen_unique(filename, "wb");
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000}; PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBFullTeamMembership), 0x00E7, 0x00000000};
phosg::fwritex(f.get(), header); phosg::fwritex(f.get(), header);
phosg::fwritex(f.get(), *character); phosg::fwritex(f.get(), *character);
phosg::fwritex(f.get(), *system); phosg::fwritex(f.get(), *system);
@@ -1202,7 +1202,7 @@ void PSOCHARFile::save(
// be used anyway, and if it's not, then it would presumably have a different // be used anyway, and if it's not, then it would presumably have a different
// set of teams with a different set of team IDs anyway, so the membership // set of teams with a different set of team IDs anyway, so the membership
// struct here would be useless either way. // struct here would be useless either way.
static const PSOBBTeamMembership empty_membership; static const PSOBBFullTeamMembership empty_membership;
phosg::fwritex(f.get(), empty_membership); phosg::fwritex(f.get(), empty_membership);
} }
+21 -14
View File
@@ -198,22 +198,29 @@ check_struct_size(SaveFileShortcutEntryGC, 0x54);
check_struct_size(SaveFileShortcutEntryXB, 0x54); check_struct_size(SaveFileShortcutEntryXB, 0x54);
check_struct_size(SaveFileShortcutEntryBB, 0xA4); check_struct_size(SaveFileShortcutEntryBB, 0xA4);
struct PSOBBTeamMembership { struct PSOBBBaseTeamMembership {
/* 0000 */ le_uint32_t team_master_guild_card_number = 0; /* 00 */ le_uint32_t team_master_guild_card_number = 0;
/* 0004 */ le_uint32_t team_id = 0; /* 04 */ le_uint32_t team_id = 0;
/* 0008 */ le_uint32_t unknown_a5 = 0; /* 08 */ le_uint32_t unknown_a5 = 0;
/* 000C */ le_uint32_t unknown_a6 = 0; /* 0C */ le_uint32_t unknown_a6 = 0;
/* 0010 */ uint8_t privilege_level = 0; /* 10 */ uint8_t privilege_level = 0;
/* 0011 */ uint8_t unknown_a7 = 0; /* 11 */ uint8_t team_member_count = 0;
/* 0012 */ uint8_t unknown_a8 = 0; /* 12 */ uint8_t unknown_a8 = 0;
/* 0013 */ uint8_t unknown_a9 = 0; /* 13 */ uint8_t unknown_a9 = 0;
/* 0014 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name; /* 14 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
/* 34 */
PSOBBBaseTeamMembership() = default;
} __packed_ws__(PSOBBBaseTeamMembership, 0x34);
struct PSOBBFullTeamMembership {
/* 0000 */ PSOBBBaseTeamMembership base;
/* 0034 */ parray<le_uint16_t, 0x20 * 0x20> flag_data; /* 0034 */ parray<le_uint16_t, 0x20 * 0x20> flag_data;
/* 0834 */ le_uint32_t reward_flags = 0; /* 0834 */ le_uint32_t reward_flags = 0;
/* 0838 */ /* 0838 */
PSOBBTeamMembership() = default; PSOBBFullTeamMembership() = default;
} __packed_ws__(PSOBBTeamMembership, 0x838); } __packed_ws__(PSOBBFullTeamMembership, 0x838);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// System files // System files
@@ -847,7 +854,7 @@ struct PSOCHARFile {
/* 0000 */ PSOCommandHeaderBB header; // command = 0x00E7, size = 0x399C, flag = 0 /* 0000 */ PSOCommandHeaderBB header; // command = 0x00E7, size = 0x399C, flag = 0
/* 0008 */ PSOBBCharacterFile character; /* 0008 */ PSOBBCharacterFile character;
/* 2EAC */ PSOBBBaseSystemFile system; /* 2EAC */ PSOBBBaseSystemFile system;
/* 3164 */ PSOBBTeamMembership team_membership; /* 3164 */ PSOBBFullTeamMembership team_membership;
/* 399C */ /* 399C */
struct LoadSharedResult { struct LoadSharedResult {
@@ -1010,7 +1017,7 @@ struct LegacySavedAccountDataBB { // .nsa file format
/* 0040 */ parray<le_uint32_t, 0x001E> blocked_senders; /* 0040 */ parray<le_uint32_t, 0x001E> blocked_senders;
/* 00B8 */ PSOBBGuildCardFile guild_card_file; /* 00B8 */ PSOBBGuildCardFile guild_card_file;
/* D648 */ PSOBBBaseSystemFile system_file; /* D648 */ PSOBBBaseSystemFile system_file;
/* D880 */ PSOBBTeamMembership team_membership; /* D880 */ PSOBBFullTeamMembership team_membership;
/* E138 */ le_uint32_t unused = 0; /* E138 */ le_uint32_t unused = 0;
/* E13C */ le_uint32_t option_flags = 0x00040058; /* E13C */ le_uint32_t option_flags = 0x00040058;
/* E140 */ parray<SaveFileShortcutEntryBB, 0x10> shortcuts; /* E140 */ parray<SaveFileShortcutEntryBB, 0x10> shortcuts;
+42 -9
View File
@@ -623,7 +623,7 @@ void send_system_file_bb(shared_ptr<Client> c) {
S_SyncSystemFile_BB_E2 cmd; S_SyncSystemFile_BB_E2 cmd;
cmd.system_file = *c->system_file(); cmd.system_file = *c->system_file();
if (team) { if (team) {
cmd.team_membership = team->membership_for_member(c->login->account->account_id); cmd.team_membership = team->full_membership_for_member(c->login->account->account_id);
} }
send_command_t(c, 0x00E2, 0x00000000, cmd); send_command_t(c, 0x00E2, 0x00000000, cmd);
} }
@@ -757,7 +757,7 @@ void send_complete_player_bb(shared_ptr<Client> c) {
cmd.char_file = *p; cmd.char_file = *p;
cmd.system_file = *sys; cmd.system_file = *sys;
if (team) { if (team) {
cmd.team_membership = team->membership_for_member(c->login->account->account_id); cmd.team_membership = team->full_membership_for_member(c->login->account->account_id);
} }
send_command_t(c, 0x00E7, 0x00000000, cmd); send_command_t(c, 0x00E7, 0x00000000, cmd);
} }
@@ -2358,7 +2358,7 @@ void send_player_join_notification(shared_ptr<Client> c,
void send_update_lobby_data_bb(std::shared_ptr<Client> c) { void send_update_lobby_data_bb(std::shared_ptr<Client> c) {
auto l = c->require_lobby(); auto l = c->require_lobby();
for (auto lc : l->clients) { for (auto lc : l->clients) {
if (lc) { if (lc && lc->version() == Version::BB_V4) {
PlayerLobbyDataBB cmd; PlayerLobbyDataBB cmd;
populate_lobby_data_for_client(cmd, c, lc); populate_lobby_data_for_client(cmd, c, lc);
send_command_t(lc, 0x00F0, 0x00000000, cmd); send_command_t(lc, 0x00F0, 0x00000000, cmd);
@@ -4122,11 +4122,7 @@ void send_team_membership_info(shared_ptr<Client> c) {
auto team = c->team(); auto team = c->team();
S_TeamMembershipInformation_BB_12EA cmd; S_TeamMembershipInformation_BB_12EA cmd;
if (team) { if (team) {
cmd.guild_card_number = c->login->account->account_id; cmd.membership = team->base_membership_for_member(c->login->account->account_id);
cmd.team_id = team->team_id;
cmd.privilege_level = team->members.at(c->login->account->account_id).privilege_level();
cmd.team_member_count = min<size_t>(team->members.size(), 100);
cmd.team_name.encode(team->name);
} }
send_command_t(c, 0x12EA, 0x00000000, cmd); send_command_t(c, 0x12EA, 0x00000000, cmd);
} }
@@ -4152,7 +4148,12 @@ static S_TeamInfoForPlayer_BB_13EA_15EA_Entry team_metadata_for_client(shared_pt
void send_update_team_metadata_for_client(shared_ptr<Client> c) { void send_update_team_metadata_for_client(shared_ptr<Client> c) {
auto l = c->require_lobby(); auto l = c->require_lobby();
send_command_t(l, 0x15EA, 0x00000001, team_metadata_for_client(c)); auto metadata = team_metadata_for_client(c);
for (auto lc : l->clients) {
if (lc && lc->version() == Version::BB_V4) {
send_command_t(lc, 0x15EA, 0x00000001, metadata);
}
}
} }
void send_all_nearby_team_metadatas_to_client(shared_ptr<Client> c, bool is_13EA) { void send_all_nearby_team_metadatas_to_client(shared_ptr<Client> c, bool is_13EA) {
@@ -4312,3 +4313,35 @@ void send_team_reward_list(shared_ptr<Client> c, bool show_purchased) {
send_command_t_vt(c, show_purchased ? 0x19EA : 0x1AEA, 0x00000000, cmd, entries); send_command_t_vt(c, show_purchased ? 0x19EA : 0x1AEA, 0x00000000, cmd, entries);
} }
void send_team_metadata_change_notifications(
shared_ptr<ServerState> s,
shared_ptr<const TeamIndex::Team> team,
uint8_t what) {
using TMC = TeamMetadataChange;
for (const auto& it : team->members) {
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
if (what & TMC::TEAM_MASTER) {
send_update_lobby_data_bb(member_c);
}
if (what & (TMC::TEAM_MASTER | TMC::TEAM_NAME)) {
send_team_membership_info(member_c);
}
if (what & (TMC::TEAM_MASTER | TMC::FLAG_DATA | TMC::TEAM_NAME)) {
send_update_team_metadata_for_client(member_c);
}
if (what & TMC::REWARD_FLAGS) {
send_update_team_reward_flags(member_c);
}
} catch (const out_of_range&) {
}
}
}
void send_team_membership_change_notifications(shared_ptr<Client> changed_c) {
send_update_lobby_data_bb(changed_c);
send_update_team_metadata_for_client(changed_c);
send_team_membership_info(changed_c);
send_update_team_reward_flags(changed_c);
}
+14
View File
@@ -455,3 +455,17 @@ void send_team_member_list(std::shared_ptr<Client> c); // 09EA
void send_intra_team_ranking(std::shared_ptr<Client> c); // 18EA void send_intra_team_ranking(std::shared_ptr<Client> c); // 18EA
void send_team_reward_list(std::shared_ptr<Client> c, bool show_purchased); // 19EA, 1AEA void send_team_reward_list(std::shared_ptr<Client> c, bool show_purchased); // 19EA, 1AEA
void send_cross_team_ranking(std::shared_ptr<Client> c); // 1CEA void send_cross_team_ranking(std::shared_ptr<Client> c); // 1CEA
enum TeamMetadataChange : uint8_t {
TEAM_MASTER = 0x01,
FLAG_DATA = 0x02,
REWARD_FLAGS = 0x04,
TEAM_NAME = 0x08,
TEAM_DISBANDED = 0xFF,
};
void send_team_metadata_change_notifications(
std::shared_ptr<ServerState> s,
std::shared_ptr<const TeamIndex::Team> team,
uint8_t what);
void send_team_membership_change_notifications(std::shared_ptr<Client> changed_c);
+9 -3
View File
@@ -129,19 +129,25 @@ void TeamIndex::Team::delete_files() const {
remove(flag_filename.c_str()); remove(flag_filename.c_str());
} }
PSOBBTeamMembership TeamIndex::Team::membership_for_member(uint32_t account_id) const { PSOBBBaseTeamMembership TeamIndex::Team::base_membership_for_member(uint32_t account_id) const {
const auto& m = this->members.at(account_id); const auto& m = this->members.at(account_id);
PSOBBTeamMembership ret; PSOBBBaseTeamMembership ret;
ret.team_master_guild_card_number = this->master_account_id; ret.team_master_guild_card_number = this->master_account_id;
ret.team_id = this->team_id; ret.team_id = this->team_id;
ret.unknown_a5 = 0; ret.unknown_a5 = 0;
ret.unknown_a6 = 0; ret.unknown_a6 = 0;
ret.privilege_level = m.privilege_level(); ret.privilege_level = m.privilege_level();
ret.unknown_a7 = 0; ret.team_member_count = this->members.size();
ret.unknown_a8 = 0; ret.unknown_a8 = 0;
ret.unknown_a9 = 0; ret.unknown_a9 = 0;
ret.team_name.encode(this->name); ret.team_name.encode(this->name);
return ret;
}
PSOBBFullTeamMembership TeamIndex::Team::full_membership_for_member(uint32_t account_id) const {
PSOBBFullTeamMembership ret;
ret.base = base_membership_for_member(account_id);
if (this->flag_data) { if (this->flag_data) {
ret.flag_data = *this->flag_data; ret.flag_data = *this->flag_data;
} else { } else {
+2 -1
View File
@@ -79,7 +79,8 @@ public:
void save_flag() const; void save_flag() const;
void delete_files() const; void delete_files() const;
PSOBBTeamMembership membership_for_member(uint32_t account_id) const; PSOBBBaseTeamMembership base_membership_for_member(uint32_t account_id) const;
PSOBBFullTeamMembership full_membership_for_member(uint32_t account_id) const;
[[nodiscard]] inline bool check_reward_flag(RewardFlag flag) const { [[nodiscard]] inline bool check_reward_flag(RewardFlag flag) const {
return !!(static_cast<uint8_t>(flag) & this->reward_flags); return !!(static_cast<uint8_t>(flag) & this->reward_flags);