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)
struct S_SyncSystemFile_BB_E2 {
PSOBBBaseSystemFile system_file;
PSOBBTeamMembership team_membership;
/* 0000 */ PSOBBBaseSystemFile system_file;
/* 02B8 */ PSOBBFullTeamMembership team_membership;
/* 0AF0 */
} __packed_ws__(S_SyncSystemFile_BB_E2, 0xAF0);
// E3 (S->C): Game or tournament info (Episode 3)
@@ -3244,7 +3245,7 @@ struct C_CreateSpectatorTeam_Ep3_E7 {
struct SC_SyncSaveFiles_BB_E7 {
/* 0000 */ PSOBBCharacterFile char_file;
/* 2EA4 */ PSOBBBaseSystemFile system_file;
/* 30DC */ PSOBBTeamMembership team_membership;
/* 315C */ PSOBBFullTeamMembership team_membership;
/* 3994 */
} __packed_ws__(SC_SyncSaveFiles_BB_E7, 0x3994);
@@ -3488,19 +3489,13 @@ struct C_ChangeTeamMemberPrivilegeLevel_BB_11EA {
} __packed_ws__(C_ChangeTeamMemberPrivilegeLevel_BB_11EA, 4);
// 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 {
le_uint32_t unknown_a1 = 0;
le_uint32_t guild_card_number = 0;
le_uint32_t team_id = 0;
le_uint32_t unknown_a4 = 0;
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;
// If skip_update_system_file is not zero, the client ignores the command.
// It's not clear why this field exists.
le_uint32_t skip_update_system_file = 0;
PSOBBBaseTeamMembership membership;
} __packed_ws__(S_TeamMembershipInformation_BB_12EA, 0x38);
// 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();
send_command(c, 0x02EA, 0x00000000);
send_update_team_metadata_for_client(c);
send_team_membership_info(c);
send_update_team_reward_flags(c);
send_team_membership_change_notifications(c);
}
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->character()->disp.name.decode(added_c->language()));
send_update_team_metadata_for_client(added_c);
send_team_membership_info(added_c);
send_team_membership_change_notifications(added_c);
send_command(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) {
send_update_team_metadata_for_client(removed_c);
send_team_membership_info(removed_c);
send_team_membership_change_notifications(removed_c);
}
} else {
// 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)) {
const auto& cmd = check_size_t<C_SetTeamFlag_BB_0FEA>(data);
s->team_index->set_flag_data(team->team_id, cmd.flag_data);
for (const auto& it : team->members) {
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&) {
}
}
send_team_metadata_change_notifications(s, team, TeamMetadataChange::FLAG_DATA);
}
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();
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);
send_command(c, 0x10EA, 0x00000000);
for (const auto& it : team->members) {
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&) {
}
}
send_team_metadata_change_notifications(s, team, TeamMetadataChange::TEAM_DISBANDED);
}
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) {
send_update_team_metadata_for_client(c);
send_team_membership_info(c);
send_team_membership_change_notifications(c);
}
if (send_updates_for_other_m) {
try {
auto other_c = s->find_client(nullptr, cmd.guild_card_number);
send_update_team_metadata_for_client(other_c);
send_team_membership_info(other_c);
send_team_membership_change_notifications(other_c);
} 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);
if (reward.reward_flag != TeamIndex::Team::RewardFlag::NONE) {
for (const auto& it : team->members) {
try {
auto member_c = s->find_client(nullptr, it.second.account_id);
send_update_team_reward_flags(member_c);
} catch (const out_of_range&) {
}
}
send_team_metadata_change_notifications(s, team, TeamMetadataChange::REWARD_FLAGS);
}
if (!reward.reward_item.empty()) {
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 {
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.account_id);
send_update_team_metadata_for_client(c);
send_team_membership_info(c);
} catch (const out_of_range&) {
}
}
send_team_metadata_change_notifications(s, team, TeamMetadataChange::TEAM_NAME);
}
break;
}
+3 -3
View File
@@ -1173,7 +1173,7 @@ PSOCHARFile::LoadSharedResult PSOCHARFile::load_shared(const string& filename, b
if (header.flag != 0x00000000) {
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;
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 PSOBBCharacterFile> character) {
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(), *character);
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
// set of teams with a different set of team IDs anyway, so the membership
// struct here would be useless either way.
static const PSOBBTeamMembership empty_membership;
static const PSOBBFullTeamMembership 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(SaveFileShortcutEntryBB, 0xA4);
struct PSOBBTeamMembership {
/* 0000 */ le_uint32_t team_master_guild_card_number = 0;
/* 0004 */ le_uint32_t team_id = 0;
/* 0008 */ le_uint32_t unknown_a5 = 0;
/* 000C */ le_uint32_t unknown_a6 = 0;
/* 0010 */ uint8_t privilege_level = 0;
/* 0011 */ uint8_t unknown_a7 = 0;
/* 0012 */ uint8_t unknown_a8 = 0;
/* 0013 */ uint8_t unknown_a9 = 0;
/* 0014 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
struct PSOBBBaseTeamMembership {
/* 00 */ le_uint32_t team_master_guild_card_number = 0;
/* 04 */ le_uint32_t team_id = 0;
/* 08 */ le_uint32_t unknown_a5 = 0;
/* 0C */ le_uint32_t unknown_a6 = 0;
/* 10 */ uint8_t privilege_level = 0;
/* 11 */ uint8_t team_member_count = 0;
/* 12 */ uint8_t unknown_a8 = 0;
/* 13 */ uint8_t unknown_a9 = 0;
/* 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;
/* 0834 */ le_uint32_t reward_flags = 0;
/* 0838 */
PSOBBTeamMembership() = default;
} __packed_ws__(PSOBBTeamMembership, 0x838);
PSOBBFullTeamMembership() = default;
} __packed_ws__(PSOBBFullTeamMembership, 0x838);
////////////////////////////////////////////////////////////////////////////////
// System files
@@ -847,7 +854,7 @@ struct PSOCHARFile {
/* 0000 */ PSOCommandHeaderBB header; // command = 0x00E7, size = 0x399C, flag = 0
/* 0008 */ PSOBBCharacterFile character;
/* 2EAC */ PSOBBBaseSystemFile system;
/* 3164 */ PSOBBTeamMembership team_membership;
/* 3164 */ PSOBBFullTeamMembership team_membership;
/* 399C */
struct LoadSharedResult {
@@ -1010,7 +1017,7 @@ struct LegacySavedAccountDataBB { // .nsa file format
/* 0040 */ parray<le_uint32_t, 0x001E> blocked_senders;
/* 00B8 */ PSOBBGuildCardFile guild_card_file;
/* D648 */ PSOBBBaseSystemFile system_file;
/* D880 */ PSOBBTeamMembership team_membership;
/* D880 */ PSOBBFullTeamMembership team_membership;
/* E138 */ le_uint32_t unused = 0;
/* E13C */ le_uint32_t option_flags = 0x00040058;
/* 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;
cmd.system_file = *c->system_file();
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);
}
@@ -757,7 +757,7 @@ void send_complete_player_bb(shared_ptr<Client> c) {
cmd.char_file = *p;
cmd.system_file = *sys;
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);
}
@@ -2358,7 +2358,7 @@ void send_player_join_notification(shared_ptr<Client> c,
void send_update_lobby_data_bb(std::shared_ptr<Client> c) {
auto l = c->require_lobby();
for (auto lc : l->clients) {
if (lc) {
if (lc && lc->version() == Version::BB_V4) {
PlayerLobbyDataBB cmd;
populate_lobby_data_for_client(cmd, c, lc);
send_command_t(lc, 0x00F0, 0x00000000, cmd);
@@ -4122,11 +4122,7 @@ void send_team_membership_info(shared_ptr<Client> c) {
auto team = c->team();
S_TeamMembershipInformation_BB_12EA cmd;
if (team) {
cmd.guild_card_number = 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);
cmd.membership = team->base_membership_for_member(c->login->account->account_id);
}
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) {
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) {
@@ -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);
}
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_team_reward_list(std::shared_ptr<Client> c, bool show_purchased); // 19EA, 1AEA
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());
}
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);
PSOBBTeamMembership ret;
PSOBBBaseTeamMembership ret;
ret.team_master_guild_card_number = this->master_account_id;
ret.team_id = this->team_id;
ret.unknown_a5 = 0;
ret.unknown_a6 = 0;
ret.privilege_level = m.privilege_level();
ret.unknown_a7 = 0;
ret.team_member_count = this->members.size();
ret.unknown_a8 = 0;
ret.unknown_a9 = 0;
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) {
ret.flag_data = *this->flag_data;
} else {
+2 -1
View File
@@ -79,7 +79,8 @@ public:
void save_flag() 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 {
return !!(static_cast<uint8_t>(flag) & this->reward_flags);