diff --git a/src/Client.cc b/src/Client.cc index eb130905..d0f42392 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -1,4 +1,5 @@ #include "Client.hh" +#include "TeamSync.hh" #include #include @@ -406,6 +407,7 @@ std::shared_ptr Client::team() const { std::string name = p->disp.visual.name.decode(this->language()); if (m.name != name) { this->log.info_f("Updating player name in team config"); + TeamSync::enqueue_team_member_update(this->login->account->account_id, name, 0); s->team_index->update_member_name(this->login->account->account_id, name); } } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index d2cdd4b1..e1bbb6e7 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -4786,7 +4786,7 @@ static void on_exchange_item_for_team_points_bb(std::shared_ptr c, Subco } auto s = c->require_server_state(); - if (TeamSync::relay_team_actions_enabled()) { + if (TeamSync::relay_team_actions_enabled() && !TeamSync::relay_team_points_enabled()) { // TeamSync phase 1 is membership-only. Team points are not yet routed // through the authority, so do not consume the item locally. return; @@ -4798,12 +4798,23 @@ static void on_exchange_item_for_team_points_bb(std::shared_ptr c, Subco size_t amount = item.stack_size(limits); size_t points = s->item_parameter_table(Version::BB_V4)->get_item_team_points(item); - s->team_index->add_member_points(c->login->account->account_id, points * amount); + uint32_t earned_points = points * amount; + if (TeamSync::relay_team_points_enabled() && + !TeamSync::enqueue_team_member_update(c->login->account->account_id, "", earned_points)) { + l->log.warning_f("Failed to enqueue TeamSync team point update for account {:08X}; returning item to inventory", + c->login->account->account_id); + item.id = l->generate_item_id(0xFF); + p->add_item(item, limits); + send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); + return; + } + + s->team_index->add_member_points(c->login->account->account_id, earned_points); if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} exchanged inventory item {}:{:08X} ({}) x{} for {} * {} = {} team points", - c->lobby_client_id, cmd.header.client_id, cmd.item_id, name, amount, points, amount, points * amount); + c->lobby_client_id, cmd.header.client_id, cmd.item_id, name, amount, points, amount, earned_points); c->print_inventory(); } diff --git a/src/TeamSync.cc b/src/TeamSync.cc index 821fc4a4..b45ac02b 100644 --- a/src/TeamSync.cc +++ b/src/TeamSync.cc @@ -42,6 +42,8 @@ struct OutboundEvent { std::string team_name; uint32_t creator_account_id = 0; std::string creator_name; + + int64_t points_delta = 0; }; static constexpr size_t MAX_OUTBOUND_EVENTS = 256; @@ -452,6 +454,11 @@ bool relay_team_chat_enabled() { return cfg.enabled && cfg.relay_team_chat; } +bool relay_team_points_enabled() { + auto cfg = get_config(); + return cfg.enabled && cfg.relay_team_points; +} + bool relay_team_actions_enabled() { auto cfg = get_config(); return cfg.enabled && cfg.relay_team_actions; @@ -511,6 +518,45 @@ bool enqueue_team_member_add(uint32_t team_id, uint32_t account_id, const std::s return true; } +bool enqueue_team_member_update(uint32_t account_id, const std::string& name, int64_t points_delta) { + auto cfg = get_config(); + if (!cfg.enabled || account_id == 0) { + return false; + } + + bool has_name = !name.empty(); + bool has_points = (points_delta != 0); + if (!has_name && !has_points) { + return false; + } + if (points_delta < 0) { + return false; + } + if (has_name && !cfg.relay_team_actions) { + return false; + } + if (has_points && !cfg.relay_team_points) { + return false; + } + + std::lock_guard g(exchange_state_mutex); + if (outbound_events.size() >= MAX_OUTBOUND_EVENTS) { + std::fprintf(stderr, + "[TeamSync] warning outbound_event_dropped reason=queue_full source=%s team_namespace=%s type=team_member_update\n", + source_label(cfg).c_str(), + cfg.team_namespace.c_str()); + return false; + } + + OutboundEvent ev; + ev.type = "team_member_update"; + ev.account_id = account_id; + ev.name = name; + ev.points_delta = points_delta; + outbound_events.emplace_back(std::move(ev)); + return true; +} + bool enqueue_team_member_remove(uint32_t account_id) { auto cfg = get_config(); if (!cfg.enabled || !cfg.relay_team_actions) { @@ -588,6 +634,15 @@ static std::string exchange_body_for_current_state(const Config& cfg) { ev.account_id, json_escape(ev.name)); + } else if (ev.type == "team_member_update") { + parts += std::format( + "{{\"seq\":{},\"type\":\"team_member_update\",\"team_namespace\":\"{}\",\"account_id\":{},\"name\":\"{}\",\"points_delta\":{}}}", + ev.seq, + json_escape(cfg.team_namespace), + ev.account_id, + json_escape(ev.name), + ev.points_delta); + } else if (ev.type == "team_member_remove") { parts += std::format( "{{\"seq\":{},\"type\":\"team_member_remove\",\"team_namespace\":\"{}\",\"account_id\":{}}}", diff --git a/src/TeamSync.hh b/src/TeamSync.hh index be9a47e2..5bd7e633 100644 --- a/src/TeamSync.hh +++ b/src/TeamSync.hh @@ -32,10 +32,12 @@ void configure_from_json(const phosg::JSON& json); bool enabled(); bool relay_team_chat_enabled(); +bool relay_team_points_enabled(); bool relay_team_actions_enabled(); bool enqueue_team_create(const std::string& team_name, uint32_t creator_account_id, const std::string& creator_name); bool enqueue_team_member_add(uint32_t team_id, uint32_t account_id, const std::string& name); +bool enqueue_team_member_update(uint32_t account_id, const std::string& name, int64_t points_delta); bool enqueue_team_member_remove(uint32_t account_id); asio::awaitable exchange_once_now();