team sync team chat relay fix
team sync team chat relay fix
This commit is contained in:
@@ -6241,6 +6241,22 @@ static asio::awaitable<void> on_EA_BB(std::shared_ptr<Client> c, Channel::Messag
|
||||
} catch (const std::out_of_range&) {
|
||||
}
|
||||
}
|
||||
|
||||
if (TeamSync::relay_team_chat_enabled()) {
|
||||
const bool queued_team_chat = TeamSync::enqueue_team_chat(
|
||||
team->team_id,
|
||||
c->login->account->account_id,
|
||||
c->character_file()->disp.visual.name.decode(c->language()),
|
||||
msg.data.data(),
|
||||
msg.data.size());
|
||||
config_log.info_f(
|
||||
"TeamSync team_chat relay attempt team_id={:08X} account_id={:08X} size={} queued={}",
|
||||
team->team_id,
|
||||
c->login->account->account_id,
|
||||
msg.data.size(),
|
||||
queued_team_chat);
|
||||
co_await TeamSync::exchange_once_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,31 @@ static constexpr bool IS_WINDOWS = true;
|
||||
static constexpr bool IS_WINDOWS = false;
|
||||
#endif
|
||||
|
||||
static uint8_t team_sync_hex_value_server_state(char ch) {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
return ch - '0';
|
||||
} else if (ch >= 'a' && ch <= 'f') {
|
||||
return 10 + ch - 'a';
|
||||
} else if (ch >= 'A' && ch <= 'F') {
|
||||
return 10 + ch - 'A';
|
||||
}
|
||||
throw std::runtime_error("invalid hex character");
|
||||
}
|
||||
|
||||
static std::string team_sync_decode_hex_string_server_state(const std::string& hex) {
|
||||
if (hex.size() & 1) {
|
||||
throw std::runtime_error("hex string has odd length");
|
||||
}
|
||||
std::string ret;
|
||||
ret.resize(hex.size() / 2);
|
||||
for (size_t z = 0; z < ret.size(); z++) {
|
||||
ret[z] = static_cast<char>(
|
||||
(team_sync_hex_value_server_state(hex[z * 2]) << 4) |
|
||||
team_sync_hex_value_server_state(hex[z * 2 + 1]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CheatFlags::CheatFlags(const phosg::JSON& json) : CheatFlags() {
|
||||
std::unordered_set<std::string> enabled_keys;
|
||||
for (const auto& it : json.as_list()) {
|
||||
@@ -1758,6 +1783,44 @@ void ServerState::load_teams() {
|
||||
config_log.warning_f("Failed to apply canonical TeamSync team state: {}", e.what());
|
||||
}
|
||||
});
|
||||
|
||||
TeamSync::set_inbound_event_callback([this](const phosg::JSON& event) -> void {
|
||||
try {
|
||||
if (!this->team_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string type = event.get_string("type", "");
|
||||
if (type != "team_chat") {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t team_id = event.get_int("team_id", 0);
|
||||
uint32_t sender_account_id = event.get_int("sender_account_id", 0);
|
||||
std::string message_data_hex = event.get_string("message_data_hex", "");
|
||||
std::string message_data = team_sync_decode_hex_string_server_state(message_data_hex);
|
||||
|
||||
auto team = this->team_index->get_by_id(team_id);
|
||||
if (!team) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& it : team->members) {
|
||||
uint32_t target_account_id = it.second.account_id;
|
||||
if (target_account_id == sender_account_id) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
auto target_c = this->find_client(nullptr, target_account_id);
|
||||
send_command(target_c, 0x07EA, 0x00000000, message_data);
|
||||
} catch (const std::out_of_range&) {
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
config_log.warning_f("Failed to apply inbound TeamSync event: {}", e.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServerState::load_patch_indexes() {
|
||||
|
||||
+77
-1
@@ -40,6 +40,10 @@ struct OutboundEvent {
|
||||
uint32_t account_id = 0;
|
||||
std::string name;
|
||||
|
||||
uint32_t sender_account_id = 0;
|
||||
std::string sender_name;
|
||||
std::string message_data_hex;
|
||||
|
||||
std::string team_name;
|
||||
uint32_t creator_account_id = 0;
|
||||
std::string creator_name;
|
||||
@@ -65,6 +69,9 @@ static std::deque<OutboundEvent> outbound_events;
|
||||
static std::mutex canonical_team_state_callback_mutex;
|
||||
static CanonicalTeamStateCallback canonical_team_state_callback;
|
||||
|
||||
static std::mutex inbound_event_callback_mutex;
|
||||
static InboundEventCallback inbound_event_callback;
|
||||
|
||||
static std::string source_label(const Config& cfg) {
|
||||
if (!cfg.source.empty()) {
|
||||
return cfg.source;
|
||||
@@ -486,6 +493,31 @@ bool relay_team_actions_enabled() {
|
||||
return cfg.enabled && cfg.relay_team_actions;
|
||||
}
|
||||
|
||||
bool enqueue_team_chat(uint32_t team_id, uint32_t sender_account_id, const std::string& sender_name, const void* data, size_t size) {
|
||||
auto cfg = get_config();
|
||||
if (!cfg.enabled || !cfg.relay_team_chat || team_id == 0 || sender_account_id == 0 || !data || !size || size > 2048) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> 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_chat\n",
|
||||
source_label(cfg).c_str(),
|
||||
cfg.team_namespace.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
OutboundEvent ev;
|
||||
ev.type = "team_chat";
|
||||
ev.team_id = team_id;
|
||||
ev.sender_account_id = sender_account_id;
|
||||
ev.sender_name = sender_name;
|
||||
ev.message_data_hex = hex_encode(data, size);
|
||||
outbound_events.emplace_back(std::move(ev));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool enqueue_team_create(const std::string& team_name, uint32_t creator_account_id, const std::string& creator_name) {
|
||||
auto cfg = get_config();
|
||||
if (!cfg.enabled || !cfg.relay_team_actions) {
|
||||
@@ -724,6 +756,22 @@ void set_canonical_team_state_callback(CanonicalTeamStateCallback cb) {
|
||||
canonical_team_state_callback = std::move(cb);
|
||||
}
|
||||
|
||||
void set_inbound_event_callback(InboundEventCallback cb) {
|
||||
std::lock_guard<std::mutex> g(inbound_event_callback_mutex);
|
||||
inbound_event_callback = std::move(cb);
|
||||
}
|
||||
|
||||
static void apply_inbound_event(const phosg::JSON& event) {
|
||||
InboundEventCallback cb;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(inbound_event_callback_mutex);
|
||||
cb = inbound_event_callback;
|
||||
}
|
||||
if (cb) {
|
||||
cb(event);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_canonical_team_state(const phosg::JSON& canonical_team_state) {
|
||||
CanonicalTeamStateCallback cb;
|
||||
{
|
||||
@@ -753,7 +801,17 @@ static std::string exchange_body_for_current_state(const Config& cfg) {
|
||||
}
|
||||
first = false;
|
||||
|
||||
if (ev.type == "team_create") {
|
||||
if (ev.type == "team_chat") {
|
||||
parts += std::format(
|
||||
"{{\"seq\":{},\"type\":\"team_chat\",\"team_namespace\":\"{}\",\"team_id\":{},\"sender_account_id\":{},\"sender_name\":\"{}\",\"message_data_hex\":\"{}\"}}",
|
||||
ev.seq,
|
||||
json_escape(cfg.team_namespace),
|
||||
ev.team_id,
|
||||
ev.sender_account_id,
|
||||
json_escape(ev.sender_name),
|
||||
ev.message_data_hex);
|
||||
|
||||
} else if (ev.type == "team_create") {
|
||||
parts += std::format(
|
||||
"{{\"seq\":{},\"type\":\"team_create\",\"team_namespace\":\"{}\",\"creator_account_id\":{},\"creator_name\":\"{}\",\"team_name\":\"{}\"}}",
|
||||
ev.seq,
|
||||
@@ -870,9 +928,27 @@ static asio::awaitable<void> run_empty_exchange_once(const Config& cfg) {
|
||||
|
||||
uint64_t ack_max_seq = response.get_int("ack_max_seq", 0);
|
||||
uint64_t next_cursor = response.get_int("next_cursor", 0);
|
||||
|
||||
const auto& stats_json = response.get("stats", phosg::JSON::dict());
|
||||
std::fprintf(stderr,
|
||||
"[TeamSync] exchange response source=%s team_namespace=%s ack_max_seq=%" PRIu64 " next_seq=%" PRIu64 " cursor=%" PRIu64 " accepted=%" PRId64 " duplicates=%" PRId64 " blocked=%" PRId64 " retained_events=%" PRId64 "\n",
|
||||
source_label(cfg).c_str(),
|
||||
cfg.team_namespace.c_str(),
|
||||
ack_max_seq,
|
||||
exchange_state.next_seq,
|
||||
next_cursor,
|
||||
stats_json.get_int("accepted", -1),
|
||||
stats_json.get_int("duplicates", -1),
|
||||
stats_json.get_int("blocked", -1),
|
||||
stats_json.get_int("retained_events", -1));
|
||||
bool truncated = response.get_bool("truncated", false);
|
||||
size_t inbound_events = response.get("events", phosg::JSON::list()).as_list().size();
|
||||
|
||||
const auto& events_json = response.get("events", phosg::JSON::list()).as_list();
|
||||
for (const auto& event_json_p : events_json) {
|
||||
apply_inbound_event(*event_json_p);
|
||||
}
|
||||
|
||||
const auto& canonical_team_state = response.get("canonical_team_state", phosg::JSON::dict());
|
||||
if (!canonical_team_state.as_dict().empty()) {
|
||||
apply_canonical_team_state(canonical_team_state);
|
||||
|
||||
@@ -36,6 +36,7 @@ bool relay_team_chat_enabled();
|
||||
bool relay_team_points_enabled();
|
||||
bool relay_team_actions_enabled();
|
||||
|
||||
bool enqueue_team_chat(uint32_t team_id, uint32_t sender_account_id, const std::string& sender_name, const void* data, size_t size);
|
||||
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);
|
||||
@@ -51,6 +52,9 @@ asio::awaitable<bool> exchange_once_now();
|
||||
using CanonicalTeamStateCallback = std::function<void(const phosg::JSON&)>;
|
||||
void set_canonical_team_state_callback(CanonicalTeamStateCallback cb);
|
||||
|
||||
using InboundEventCallback = std::function<void(const phosg::JSON&)>;
|
||||
void set_inbound_event_callback(InboundEventCallback cb);
|
||||
|
||||
void start_exchange_task(asio::io_context& io_context);
|
||||
|
||||
} // namespace TeamSync
|
||||
|
||||
Reference in New Issue
Block a user