|
|
|
@ -3,6 +3,7 @@ |
|
|
|
#include "gui_bridge.h" |
|
|
|
#include "gui_bridge.h" |
|
|
|
#include "topo_node_sqlite.h" |
|
|
|
#include "topo_node_sqlite.h" |
|
|
|
#include "member_sync.h" |
|
|
|
#include "member_sync.h" |
|
|
|
|
|
|
|
#include "merkle_sync.h" |
|
|
|
|
|
|
|
|
|
|
|
#include "../../../src/utun_instance.h" |
|
|
|
#include "../../../src/utun_instance.h" |
|
|
|
#include "../../../src/etcp_api.h" |
|
|
|
#include "../../../src/etcp_api.h" |
|
|
|
@ -269,7 +270,9 @@ struct chat_sync { |
|
|
|
void* refresh_timer; |
|
|
|
void* refresh_timer; |
|
|
|
void* info_req_timer; |
|
|
|
void* info_req_timer; |
|
|
|
void* join_timer; |
|
|
|
void* join_timer; |
|
|
|
|
|
|
|
void* sync_timer; |
|
|
|
uint8_t initialized; |
|
|
|
uint8_t initialized; |
|
|
|
|
|
|
|
uint8_t sync_scheduled; |
|
|
|
uint64_t pending_invite_ch_id; |
|
|
|
uint64_t pending_invite_ch_id; |
|
|
|
uint64_t pending_invite_node_id; |
|
|
|
uint64_t pending_invite_node_id; |
|
|
|
}; |
|
|
|
}; |
|
|
|
@ -277,6 +280,7 @@ struct chat_sync { |
|
|
|
#define CS_ID "chat_sync" |
|
|
|
#define CS_ID "chat_sync" |
|
|
|
#define CS_INFO_REQ_TIMEOUT_MS 5000 |
|
|
|
#define CS_INFO_REQ_TIMEOUT_MS 5000 |
|
|
|
#define CS_JOIN_TIMEOUT_MS 8000 |
|
|
|
#define CS_JOIN_TIMEOUT_MS 8000 |
|
|
|
|
|
|
|
#define CS_SYNC_INTERVAL_MS 1000 |
|
|
|
|
|
|
|
|
|
|
|
static const char* cs_msg_name(uint8_t type) { |
|
|
|
static const char* cs_msg_name(uint8_t type) { |
|
|
|
switch (type) { |
|
|
|
switch (type) { |
|
|
|
@ -483,6 +487,87 @@ static void cs_post_channel_online(struct chat_sync* cs, const char* ch_id) { |
|
|
|
gui_bridge_post(GUI_EVT_CHANNEL_PEERS_ONLINE, evt, 1 + (int)cl + 2); |
|
|
|
gui_bridge_post(GUI_EVT_CHANNEL_PEERS_ONLINE, evt, 1 + (int)cl + 2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Throttled sync (не чаще CS_SYNC_INTERVAL_MS) ── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_flush_sync(struct chat_sync* cs) { |
|
|
|
|
|
|
|
uint64_t myid = cs->inst->node_id; |
|
|
|
|
|
|
|
for (int i = 0; i < cs->channel_count; i++) { |
|
|
|
|
|
|
|
struct channel_cache* ch = &cs->channels[i]; |
|
|
|
|
|
|
|
for (int j = 0; j < ch->peer_count; j++) { |
|
|
|
|
|
|
|
uint64_t pid = ch->peer_ids[j]; |
|
|
|
|
|
|
|
if (pid == myid || !cs_is_peer_online(cs->inst, pid)) continue; |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: flush_sync start for peer=%016llx ch=%s", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)pid, ch->channel_id); |
|
|
|
|
|
|
|
member_sync_start(cs->inst, pid, ch->channel_id, NULL, NULL); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_sync_timer_cb(void* arg) { |
|
|
|
|
|
|
|
struct chat_sync* cs = (struct chat_sync*)arg; |
|
|
|
|
|
|
|
if (!cs || !cs->initialized) return; |
|
|
|
|
|
|
|
cs->sync_timer = NULL; |
|
|
|
|
|
|
|
cs->sync_scheduled = 0; |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: sync timer fire", CS_ID); |
|
|
|
|
|
|
|
cs_flush_sync(cs); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_schedule_sync(struct chat_sync* cs) { |
|
|
|
|
|
|
|
if (cs->sync_scheduled) return; |
|
|
|
|
|
|
|
cs->sync_scheduled = 1; |
|
|
|
|
|
|
|
cs->sync_timer = uasync_set_timeout(cs->inst->ua, |
|
|
|
|
|
|
|
CS_SYNC_INTERVAL_MS * 10, cs, cs_sync_timer_cb, "cs_sync"); |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: sync scheduled in %ums", CS_ID, CS_SYNC_INTERVAL_MS); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Unified peer status change (локальная БД + GUI + push_update + schedule_sync) ── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_on_remote_status_changed(uint64_t peer, int online) { |
|
|
|
|
|
|
|
if (!g_cs) return; |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: remote status changed peer=%016llx online=%d", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)peer, online); |
|
|
|
|
|
|
|
for (int i = 0; i < g_cs->channel_count; i++) { |
|
|
|
|
|
|
|
for (int j = 0; j < g_cs->channels[i].peer_count; j++) { |
|
|
|
|
|
|
|
if (g_cs->channels[i].peer_ids[j] == peer) { |
|
|
|
|
|
|
|
size_t cl = strlen(g_cs->channels[i].channel_id); |
|
|
|
|
|
|
|
if (cl > 63) cl = 63; |
|
|
|
|
|
|
|
uint8_t evt[65]; evt[0] = (uint8_t)cl; |
|
|
|
|
|
|
|
memcpy(evt + 1, g_cs->channels[i].channel_id, cl); |
|
|
|
|
|
|
|
gui_bridge_post(GUI_EVT_MEMBERS_CHANGED, evt, 1 + (int)cl); |
|
|
|
|
|
|
|
cs_post_channel_online(g_cs, g_cs->channels[i].channel_id); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_on_peer_status_changed(uint64_t peer, int online) { |
|
|
|
|
|
|
|
/* 1. Локальная БД */ |
|
|
|
|
|
|
|
member_sync_set_online(g_cs->inst, peer, online); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 2. GUI + online-бар */ |
|
|
|
|
|
|
|
for (int i = 0; i < g_cs->channel_count; i++) { |
|
|
|
|
|
|
|
for (int j = 0; j < g_cs->channels[i].peer_count; j++) { |
|
|
|
|
|
|
|
if (g_cs->channels[i].peer_ids[j] == peer) { |
|
|
|
|
|
|
|
size_t cl = strlen(g_cs->channels[i].channel_id); |
|
|
|
|
|
|
|
if (cl > 63) cl = 63; |
|
|
|
|
|
|
|
uint8_t evt[65]; evt[0] = (uint8_t)cl; |
|
|
|
|
|
|
|
memcpy(evt + 1, g_cs->channels[i].channel_id, cl); |
|
|
|
|
|
|
|
gui_bridge_post(GUI_EVT_MEMBERS_CHANGED, evt, 1 + (int)cl); |
|
|
|
|
|
|
|
cs_post_channel_online(g_cs, g_cs->channels[i].channel_id); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 3. Рассылка дельты всем synced-соседям */ |
|
|
|
|
|
|
|
uint8_t st = (uint8_t)online; |
|
|
|
|
|
|
|
merkle_sync_push_update(g_cs->inst, g_cs->channels[i].channel_id, peer, 0x01, &st, 1); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 4. Запланировать member_sync_start (throttled) */ |
|
|
|
|
|
|
|
cs_schedule_sync(g_cs); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void _on_member_sync_done(uint64_t peer, const char* ns, int result, void* arg) { |
|
|
|
static void _on_member_sync_done(uint64_t peer, const char* ns, int result, void* arg) { |
|
|
|
struct channel_cache* ch = (struct channel_cache*)arg; |
|
|
|
struct channel_cache* ch = (struct channel_cache*)arg; |
|
|
|
if (result == MT_OK && ch) ch->synced = CS_SYNC_DONE; |
|
|
|
if (result == MT_OK && ch) ch->synced = CS_SYNC_DONE; |
|
|
|
@ -517,11 +602,7 @@ static void cs_on_conn_up(struct ETCP_CONN* conn, void* arg) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
member_sync_set_online(g_cs->inst, peer, 1); |
|
|
|
cs_on_peer_status_changed(peer, 1); |
|
|
|
for (int i = 0; i < g_cs->channel_count; i++) { |
|
|
|
|
|
|
|
for (int j = 0; j < g_cs->channels[i].peer_count; j++) |
|
|
|
|
|
|
|
if (g_cs->channels[i].peer_ids[j] == peer) { cs_post_channel_online(g_cs, g_cs->channels[i].channel_id); break; } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { |
|
|
|
static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { |
|
|
|
@ -529,6 +610,9 @@ static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { |
|
|
|
if (!conn || !g_cs) return; |
|
|
|
if (!conn || !g_cs) return; |
|
|
|
uint64_t peer = conn->peer_node_id; |
|
|
|
uint64_t peer = conn->peer_node_id; |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: conn_down peer=%016llx", CS_ID, (unsigned long long)peer); |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: conn_down peer=%016llx", CS_ID, (unsigned long long)peer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cs_on_peer_status_changed(peer, 0); |
|
|
|
|
|
|
|
|
|
|
|
uint16_t rtt = conn->rtt_avg_100; |
|
|
|
uint16_t rtt = conn->rtt_avg_100; |
|
|
|
if (rtt > 0 && peer != 0 && g_cs->inst->topo_sqlite_db) { |
|
|
|
if (rtt > 0 && peer != 0 && g_cs->inst->topo_sqlite_db) { |
|
|
|
sqlite3* db = g_cs->inst->topo_sqlite_db; |
|
|
|
sqlite3* db = g_cs->inst->topo_sqlite_db; |
|
|
|
@ -552,7 +636,6 @@ static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { |
|
|
|
uint8_t rem[9]; rem[0] = CS_MSG_PEER_REMOVE; |
|
|
|
uint8_t rem[9]; rem[0] = CS_MSG_PEER_REMOVE; |
|
|
|
memcpy(rem + 1, &peer, 8); |
|
|
|
memcpy(rem + 1, &peer, 8); |
|
|
|
cs_propagate(g_cs, g_cs->channels[i].channel_id, peer, rem, 9); |
|
|
|
cs_propagate(g_cs, g_cs->channels[i].channel_id, peer, rem, 9); |
|
|
|
cs_post_channel_online(g_cs, g_cs->channels[i].channel_id); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -653,6 +736,7 @@ int chat_sync_init(struct UTUN_INSTANCE* inst, |
|
|
|
cs->join_timer = NULL; |
|
|
|
cs->join_timer = NULL; |
|
|
|
|
|
|
|
|
|
|
|
member_sync_init(inst); |
|
|
|
member_sync_init(inst); |
|
|
|
|
|
|
|
member_sync_set_node_updated_cb(cs_on_remote_status_changed); |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: initialized", CS_ID); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: initialized", CS_ID); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
@ -663,6 +747,8 @@ void chat_sync_destroy(struct UTUN_INSTANCE* inst) { |
|
|
|
if (!cs || !inst) return; |
|
|
|
if (!cs || !inst) return; |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: destroy", CS_ID); |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: destroy", CS_ID); |
|
|
|
chat_sync_auto_connect_stop(); |
|
|
|
chat_sync_auto_connect_stop(); |
|
|
|
|
|
|
|
if (cs->sync_timer) { uasync_cancel_timeout(inst->ua, cs->sync_timer); cs->sync_timer = NULL; } |
|
|
|
|
|
|
|
if (cs->sync_scheduled) { cs->sync_scheduled = 0; cs_flush_sync(cs); } |
|
|
|
member_sync_destroy(inst); |
|
|
|
member_sync_destroy(inst); |
|
|
|
cs->initialized = 0; g_cs = NULL; |
|
|
|
cs->initialized = 0; g_cs = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|