hello mr fox

This commit is contained in:
Green Sky 2023-08-03 22:27:19 +02:00
commit 7e90e4da33
No known key found for this signature in database
7 changed files with 904 additions and 0 deletions

22
CMakeLists.txt Normal file
View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(solanaceae)
add_library(solanaceae_zox
./solanaceae/zox/ngc.hpp
./solanaceae/zox/ngc.cpp
./solanaceae/zox/ngc_hs.hpp
./solanaceae/zox/ngc_hs.cpp
)
target_include_directories(solanaceae_zox PUBLIC .)
target_compile_features(solanaceae_zox PUBLIC cxx_std_17)
target_link_libraries(solanaceae_zox PUBLIC
solanaceae_util
solanaceae_message3
solanaceae_toxcore
solanaceae_tox_contacts
solanaceae_tox_messages
)

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
The Code is under the following License, if not stated otherwise:
MIT License
Copyright (c) 2023 Erik Scholz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
README.md Normal file
View File

@ -0,0 +1,4 @@
`plant !`
provides zox stuff

220
solanaceae/zox/ngc.cpp Normal file
View File

@ -0,0 +1,220 @@
#include "./ngc.hpp"
#include <optional>
#include <tuple>
#include <iostream>
constexpr size_t zox_magic_size = 6;
static bool is_zox_magic(const uint8_t* data, size_t size) {
//0x667788113435
return size >= zox_magic_size &&
data != nullptr &&
data[0] == 0x66 &&
data[1] == 0x77 &&
data[2] == 0x88 &&
data[3] == 0x11 &&
data[4] == 0x34 &&
data[5] == 0x35;
}
constexpr size_t zox_header_size = zox_magic_size + 2;
std::optional<std::pair<uint8_t, uint8_t>> parse_zox_pkg_header(const uint8_t* data, size_t size) {
if (!is_zox_magic(data, size)) {
return std::nullopt;
}
data += zox_magic_size;
size -= zox_magic_size;
if (size < 2) {
return std::nullopt;
}
const uint8_t version = data[0];
const uint8_t pkt_id = data[1];
return std::make_pair(version, pkt_id);
}
void ZoxNGCEventProvider::subscribeToEvents(void) {
_tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_CUSTOM_PACKET);
_tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET);
}
ZoxNGCEventProvider::ZoxNGCEventProvider(ToxEventProviderI& tep) : _tep(tep) {
subscribeToEvents();
}
bool ZoxNGCEventProvider::onZoxGroupEvent(
uint32_t group_number, uint32_t peer_number,
uint8_t version, uint8_t pkt_id,
const uint8_t* data, size_t data_size,
bool _private
) {
if (version == 0x01 && pkt_id == 0x01) {
// ngch_request
return parse_ngch_request(group_number, peer_number, data, data_size, _private);
} else if (version == 0x01 && pkt_id == 0x02) {
// ngch_syncmsg
return parse_ngch_syncmsg(group_number, peer_number, data, data_size, _private);
} else if (version == 0x01 && pkt_id == 0x03) {
std::cout << "ZOX waring: ngch_syncmsg_file not implemented\n";
} else if (version == 0x01 && pkt_id == 0x11) {
std::cout << "ZOX waring: ngc_ft not implemented\n";
} else {
std::cout << "ZOX waring: unknown packet v" << (int)version << " id" << (int)pkt_id <<"\n";
}
return false;
}
bool ZoxNGCEventProvider::parse_ngch_request(
uint32_t group_number, uint32_t peer_number,
const uint8_t* data, size_t data_size,
bool _private
) {
if (data_size > 1) {
std::cerr << "ZOX ngch_request has wrong size, should: <=1 , is: " << data_size << "\n";
return false;
}
uint8_t sync_delta = 130u;
if (data_size == 1) {
sync_delta = data[0];
// clamp
if (sync_delta < 5u) {
sync_delta = 5u;
} else if (sync_delta > 130u) {
sync_delta = 130u;
}
}
return dispatch(
ZoxNGC_Event::ngch_request,
Events::ZoxNGC_ngch_request{
group_number,
peer_number,
_private,
sync_delta
}
);
}
bool ZoxNGCEventProvider::parse_ngch_syncmsg(
uint32_t group_number, uint32_t peer_number,
const uint8_t* data, size_t data_size,
bool _private
) {
constexpr size_t min_pkg_size = 4 + 32 + 4 + 25;
if (data_size <= 4 + 32 + 4 + 25) {
std::cerr << "ZOX ngch_syncmsg has wrong size, should: >=" << min_pkg_size << " , is: " << data_size << "\n";
return false;
}
// 4 bytes, message id
uint32_t message_id = 0;
message_id |= uint32_t(data[0]) << 8*3;
message_id |= uint32_t(data[1]) << 8*2;
message_id |= uint32_t(data[2]) << 8*1;
message_id |= uint32_t(data[3]) << 8*0;
data += 4;
data_size -= 4;
// 32 bytes, sender pub key
std::array<uint8_t, 32> sender_pub_key {
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31],
};
data += 32;
data_size -= 32;
// 4 bytes, timestamp
uint32_t timestamp = 0;
timestamp |= uint32_t(data[0]) << 8*3;
timestamp |= uint32_t(data[1]) << 8*2;
timestamp |= uint32_t(data[2]) << 8*1;
timestamp |= uint32_t(data[3]) << 8*0;
data += 4;
data_size -= 4;
// 25 bytes, sender name, truncated/filled with 0
std::string_view sender_name{reinterpret_cast<const char*>(data), 25};
sender_name = sender_name.substr(0, sender_name.find_first_of('\0')); // trim \0
data += 25;
data_size -= 25;
// up to 39927 bytes, message
std::string_view message_text{reinterpret_cast<const char*>(data), data_size};
message_text = message_text.substr(0, message_text.find_first_of('\0')); // trim \0
return dispatch(
ZoxNGC_Event::ngch_syncmsg,
Events::ZoxNGC_ngch_syncmsg{
group_number,
peer_number,
_private,
message_id,
sender_pub_key,
timestamp,
sender_name,
message_text
}
);
}
bool ZoxNGCEventProvider::onToxEvent(const Tox_Event_Group_Custom_Packet* e) {
const uint32_t group_number = tox_event_group_custom_packet_get_group_number(e);
const uint32_t peer_number = tox_event_group_custom_packet_get_peer_id(e);
const uint8_t* data = tox_event_group_custom_packet_get_data(e);
size_t size = tox_event_group_custom_packet_get_data_length(e);
auto res_opt = parse_zox_pkg_header(data, size);
if (!res_opt) {
return false;
}
auto [version, pkt_id] = *res_opt;
data += zox_header_size;
size -= zox_header_size;
return onZoxGroupEvent(
group_number, peer_number,
version, pkt_id,
data, size,
false
);
}
bool ZoxNGCEventProvider::onToxEvent(const Tox_Event_Group_Custom_Private_Packet* e) {
const uint32_t group_number = tox_event_group_custom_private_packet_get_group_number(e);
const uint32_t peer_number = tox_event_group_custom_private_packet_get_peer_id(e);
const uint8_t* data = tox_event_group_custom_private_packet_get_data(e);
size_t size = tox_event_group_custom_private_packet_get_data_length(e);
auto res_opt = parse_zox_pkg_header(data, size);
if (!res_opt) {
return false;
}
auto [version, pkt_id] = *res_opt;
data += zox_header_size;
size -= zox_header_size;
return onZoxGroupEvent(
group_number, peer_number,
version, pkt_id,
data, size,
true
);
}

121
solanaceae/zox/ngc.hpp Normal file
View File

@ -0,0 +1,121 @@
#pragma once
#include <solanaceae/toxcore/tox_event_interface.hpp>
#include <solanaceae/util/event_provider.hpp>
#include <array>
#include <vector>
// fwd
//struct ToxI;
// zoff ngc history sync (draft1?)
// https://gist.github.com/zoff99/81917ddb2e55b2ce602cac4772a7b68c
namespace Events {
struct ZoxNGC_ngch_request {
uint32_t group_number {0u};
uint32_t peer_number {0u};
bool _private {true};
uint8_t sync_delta {130u};
};
struct ZoxNGC_ngch_syncmsg {
uint32_t group_number {0u};
uint32_t peer_number {0u};
bool _private {true};
uint32_t message_id {0u};
// TODO: span
std::array<uint8_t, 32> sender_pub_key;
uint32_t timestamp {0u};
std::string_view sender_name;
std::string_view message_text;
};
} // Events
enum class ZoxNGC_Event : uint32_t {
// hs
v0x01_id0x01 = 0,
ngch_request = v0x01_id0x01,
v0x01_id0x02,
ngch_syncmsg = v0x01_id0x02,
v0x01_id0x03,
ngch_syncmsg_file = v0x01_id0x03,
//v0x01_id0x04,
//v0x01_id0x05,
//v0x01_id0x06,
//v0x01_id0x07,
//v0x01_id0x08,
//v0x01_id0x09,
//v0x01_id0x0a,
//v0x01_id0x0b,
//v0x01_id0x0c,
//v0x01_id0x0d,
//v0x01_id0x0e,
//v0x01_id0x0f,
//v0x01_id0x10,
v0x01_id0x11,
ngch_ft = v0x01_id0x11,
// ...
// v0x02_id0x01
MAX
};
static_assert(size_t(ZoxNGC_Event::v0x01_id0x02) == size_t(ZoxNGC_Event::v0x01_id0x01) + 1u);
struct ZoxNGCEventI {
using enumType = ZoxNGC_Event;
virtual bool onEvent(const Events::ZoxNGC_ngch_request&) { return false; }
virtual bool onEvent(const Events::ZoxNGC_ngch_syncmsg&) { return false; }
};
using ZoxNGCEventProviderI = EventProviderI<ZoxNGCEventI>;
class ZoxNGCEventProvider : public ToxEventI, public ZoxNGCEventProviderI {
ToxEventProviderI& _tep;
//ToxI& _t;
void subscribeToEvents(void); // private
public:
ZoxNGCEventProvider(ToxEventProviderI& tep/*, ToxI& t*/);
protected:
bool onZoxGroupEvent(
uint32_t group_number, uint32_t peer_number,
uint8_t version, uint8_t pkt_id,
const uint8_t* data, size_t data_size,
bool _private
);
bool parse_ngch_request(
uint32_t group_number, uint32_t peer_number,
const uint8_t* data, size_t data_size,
bool _private
);
bool parse_ngch_syncmsg(
uint32_t group_number, uint32_t peer_number,
const uint8_t* data, size_t data_size,
bool _private
);
protected:
bool onToxEvent(const Tox_Event_Group_Custom_Packet* e) override;
bool onToxEvent(const Tox_Event_Group_Custom_Private_Packet* e) override;
};

412
solanaceae/zox/ngc_hs.cpp Normal file
View File

@ -0,0 +1,412 @@
#include "./ngc_hs.hpp"
#include <solanaceae/toxcore/tox_interface.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
#include <solanaceae/tox_contacts/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/components.hpp>
#include <optional>
#include <chrono>
#include <iostream>
#include <variant>
#include <vector>
void ZoxNGCHistorySync::subscribeToEvents(void) {
_zngcepi.subscribe(this, ZoxNGC_Event::ngch_request);
_zngcepi.subscribe(this, ZoxNGC_Event::ngch_syncmsg);
_tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_JOIN);
}
ZoxNGCHistorySync::ZoxNGCHistorySync(ToxEventProviderI& tep, ZoxNGCEventProviderI& zngcepi, ToxI& t, Contact3Registry& cr, ToxContactModel2& tcm, RegistryMessageModel& rmm)
: _tep(tep), _zngcepi(zngcepi), _t(t), _cr(cr), _tcm(tcm), _rmm(rmm), _rng(std::random_device{}())
{
subscribeToEvents();
}
void ZoxNGCHistorySync::tick(float delta) {
// send queued requests
for (auto it = _request_queue.begin(); it != _request_queue.end();) {
it->second.timer += delta;
if (it->second.timer >= it->second.delay) {
if (!_cr.all_of<Contact::Components::ToxGroupPeerEphemeral>(it->first)) {
// peer nolonger online
it = _request_queue.erase(it);
continue;
}
const auto [group_number, peer_number] = _cr.get<Contact::Components::ToxGroupPeerEphemeral>(it->first);
if (sendRequest(group_number, peer_number, it->second.sync_delta)) {
// on success, requeue with longer delay (minutes)
it->second.timer = 0.f;
it->second.delay = _delay_next_request_min + _rng_dist(_rng)*_delay_next_request_add;
// double the delay for overlap (9m-15m)
// TODO: finetune
it->second.sync_delta = uint8_t((it->second.delay/60.f)*2.f) + 1;
std::cout << "ZOX #### requeued request in " << it->second.delay << "s\n";
it++;
} else {
// on failure, assume disconnected
it = _request_queue.erase(it);
}
} else {
it++;
}
}
for (auto it = _sync_queue.begin(); it != _sync_queue.end();) {
it->second.timer += delta;
if (it->second.timer >= it->second.delay) {
Message3 msg_e = it->second.ents.front();
it->second.ents.pop();
if (!_cr.all_of<Contact::Components::ToxGroupPeerEphemeral>(it->first)) {
// peer nolonger online
it = _sync_queue.erase(it);
continue;
}
const auto [group_number, peer_number] = _cr.get<Contact::Components::ToxGroupPeerEphemeral>(it->first);
auto* reg_ptr = _rmm.get(it->first);
if (reg_ptr == nullptr) {
//std::cout << "°°°°°°°° no reg for contact\n";
it = _sync_queue.erase(it);
continue;
}
Message3Registry& reg = *reg_ptr;
if (!reg.valid(msg_e)) {
std::cerr << "ZOX NGCHS error: invalid message in sync send queue\n";
it = _sync_queue.erase(it);
continue;
}
const auto& msg_sender = reg.get<Message::Components::ContactFrom>(msg_e).c;
if (!_cr.all_of<Contact::Components::ToxGroupPeerPersistent>(msg_sender)) {
std::cerr << "ZOX NGCHS error: msg sender without persistant\n";
continue;
}
//if (auto peer_persist_opt = _cm.toPersistent(msg_sender); peer_persist_opt.has_value() && std::holds_alternative<ContactGroupPeerPersistent>(peer_persist_opt.value())) {
// get name for peer
// TODO: make sure there is no alias leaked
//const auto msg_sender_name = _cm.getContactName(msg_sender);
std::string_view msg_sender_name;
if (_cr.all_of<Contact::Components::Name>(msg_sender)) {
msg_sender_name = _cr.get<Contact::Components::Name>(msg_sender).name;
}
if (!sendSyncMessage(
group_number,
peer_number,
reg.get<Message::Components::ToxGroupMessageID>(msg_e).id,
_cr.get<Contact::Components::ToxGroupPeerPersistent>(msg_sender).peer_key.data,
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::milliseconds{reg.get<Message::Components::Timestamp>(msg_e).ts}).count(),
msg_sender_name,
reg.get<Message::Components::MessageText>(msg_e).text
) || it->second.ents.empty()) {
it = _sync_queue.erase(it);
continue;
}
}
it++;
}
}
bool ZoxNGCHistorySync::sendRequest(
uint32_t group_number, uint32_t peer_number,
uint8_t sync_delta
) {
std::vector<uint8_t> packet;
{ // magic
//0x667788113435
packet.push_back(0x66);
packet.push_back(0x77);
packet.push_back(0x88);
packet.push_back(0x11);
packet.push_back(0x34);
packet.push_back(0x35);
}
packet.push_back(0x01); // version
packet.push_back(0x01); // pkt_id
packet.push_back(sync_delta);
auto ret = _t.toxGroupSendCustomPrivatePacket(group_number, peer_number, true, packet);
// TODO: log error
return ret == TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_OK;
}
bool ZoxNGCHistorySync::sendSyncMessage(
uint32_t group_number, uint32_t peer_number,
uint32_t message_id,
const std::array<uint8_t, 32>& sender_pub_key,
uint32_t timestamp,
std::string_view sender_name,
std::string_view message_text
) {
std::vector<uint8_t> packet;
{ // magic
//0x667788113435
packet.push_back(0x66);
packet.push_back(0x77);
packet.push_back(0x88);
packet.push_back(0x11);
packet.push_back(0x34);
packet.push_back(0x35);
}
packet.push_back(0x01); // version
packet.push_back(0x02); // pkt_id
// 4 bytes, message id
packet.push_back(0xff & (message_id >> 8*3));
packet.push_back(0xff & (message_id >> 8*2));
packet.push_back(0xff & (message_id >> 8*1));
packet.push_back(0xff & (message_id >> 8*0));
// 32 bytes, sender pub key
packet.insert(packet.end(), sender_pub_key.cbegin(), sender_pub_key.cend());
// 4 bytes, timestamp
packet.push_back(0xff & (timestamp >> 8*3));
packet.push_back(0xff & (timestamp >> 8*2));
packet.push_back(0xff & (timestamp >> 8*1));
packet.push_back(0xff & (timestamp >> 8*0));
// 25 bytes, sender name, truncated/filled with 0
// TODO: handle unicode properly
for (size_t i = 0; i < 25; i++) {
if (i < sender_name.size()) {
packet.push_back(sender_name.at(i));
} else {
packet.push_back('\0');
}
}
// up to 39927 bytes, message
#if 0
packet.insert(packet.end(), message_text.cbegin(), message_text.cend());
#else
//const int64_t msg_max_possible_size = _t.toxGroup
// TODO: make pr and add functions
const uint64_t msg_max_possible_size = std::clamp<int64_t>(
TOX_GROUP_MAX_CUSTOM_LOSSLESS_PACKET_LENGTH - packet.size(),
0, // low
39927 // high
);
for (size_t i = 0; i < msg_max_possible_size && i < message_text.size(); i++) {
packet.push_back(message_text.at(i));
}
#endif
auto ret = _t.toxGroupSendCustomPrivatePacket(group_number, peer_number, true, packet);
// TODO: log error
return ret == TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_OK;
}
bool ZoxNGCHistorySync::onEvent(const Events::ZoxNGC_ngch_request& e) {
std::cout << "ZOX ngch_request"
<< " grp:" << e.group_number
<< " per:" << e.peer_number
<< " prv:" << e._private
<< " sdl:" << (int)e.sync_delta
<< "\n";
// if blacklisted / on cool down
const auto request_sender = _tcm.getContactGroupPeer(e.group_number, e.peer_number);
if (_sync_queue.count(request_sender)) {
std::cerr << "ZNGCHS waring: ngch_request but still in sync send queue\n";
return true;
}
// const -> dont create (this is a request for existing messages)
auto* reg_ptr = static_cast<const RegistryMessageModel&>(_rmm).get(request_sender);
if (reg_ptr == nullptr) {
std::cerr << "ZNGCHS error: group without reg\n";
return true;
}
const Message3Registry& reg = *reg_ptr;
std::queue<Message3> msg_send_queue;
// convert sync delta to ms
const int64_t sync_delta_offset_ms = int64_t(e.sync_delta) * 1000 * 60;
const uint64_t ts_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() - sync_delta_offset_ms;
auto view = reg.view<Message::Components::ContactFrom, Message::Components::ContactTo, Message::Components::Timestamp, Message::Components::MessageText, Message::Components::ToxGroupMessageID>().use<Message::Components::Timestamp>();
view.each([&](const Message3 e, const auto&, const auto& c_t, const auto& ts, const auto&, const auto&) {
// private
if (!_cr.all_of<Contact::Components::TagBig>(c_t.c)) {
return;
}
if (ts.ts < ts_start) {
std::cout << "---- " << ts.ts << " < " << ts_start << " -> too old\n";
return;
}
std::cout << "---- " << ts.ts << " >= " << ts_start << " -> selected\n";
msg_send_queue.push(e);
});
std::cout << "ZOX ngch_request selected " << msg_send_queue.size() << " messages\n";
if (!msg_send_queue.empty()) {
_sync_queue[request_sender] = SyncQueueInfo{
_delay_between_syncs_min + _rng_dist(_rng)*_delay_between_syncs_add,
0.f,
std::move(msg_send_queue)
};
}
return true;
}
bool ZoxNGCHistorySync::onEvent(const Events::ZoxNGC_ngch_syncmsg& e) {
std::cout << "ZOX ngch_syncmsg"
// who sent the syncmsg
<< " grp:" << e.group_number
<< " per:" << e.peer_number
<< " prv:" << e._private
// its contents
<< " mid:" << e.message_id
<< " spk:" << std::hex << (uint16_t)e.sender_pub_key[0] << (uint16_t)e.sender_pub_key[1] << std::dec
<< " ts:" << e.timestamp
<< " snm:" << e.sender_name
<< " txt:" << e.message_text
<< "\n";
auto sync_by_c = _tcm.getContactGroupPeer(e.group_number, e.peer_number);
assert(static_cast<bool>(sync_by_c));
auto* reg_ptr = _rmm.get(sync_by_c);
if (reg_ptr == nullptr) {
std::cerr << "ZNGCHS error: group without reg\n";
return false;
}
Message3Registry& reg = *reg_ptr;
const auto sync_c = _tcm.getContactGroupPeer(e.group_number, ToxKey{e.sender_pub_key.data(), e.sender_pub_key.size()});
assert(static_cast<bool>(sync_c)); // TODO: make conditional
// convert to ms
uint64_t sync_ts = std::chrono::milliseconds(std::chrono::seconds{e.timestamp}).count(); // o.o
uint64_t now_ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
// find matches
Message3 matching_e = entt::null;
{
const auto view = reg.view<Message::Components::ToxGroupMessageID, Message::Components::ContactFrom, Message::Components::Timestamp>();
for (const auto ent : view.use<Message::Components::Timestamp>()) {
if (view.get<Message::Components::ToxGroupMessageID>(ent).id != e.message_id) {
continue;
}
// how far apart the 2 timestamps can be, before they are considered different messages
if (std::abs(int64_t(view.get<Message::Components::Timestamp>(ent).ts) - int64_t(sync_ts)) > _max_age_difference_ms) {
std::cout << "ZOX NGCHS info: same message id, but different timestamp\n";
continue;
}
const auto& ent_c = view.get<Message::Components::ContactFrom>(ent).c;
if (!(ent_c == sync_c)) {
std::cout << "ZOX NGCHS info: same message id, but different sender\n";
continue;
}
matching_e = ent;
break; // TODO: matching list
}
}
if (reg.valid(matching_e)) {
// TODO: do something else, like average?, trust mods more?
const bool has_tw = reg.all_of<Message::Components::TimestampWritten>(matching_e);
auto& msg_ts_w = reg.get_or_emplace<Message::Components::TimestampWritten>(matching_e, sync_ts);
if (has_tw) {
if (msg_ts_w.ts > sync_ts) {
msg_ts_w.ts = sync_ts;
reg.emplace_or_replace<Message::Components::Timestamp>(matching_e, sync_ts);
// TODO: resort
//_rmm.resort({ContactGroupEphemeral{e.group_number}});
_rmm.throwEventUpdate(reg, matching_e);
}
} else {
// TODO: actually, dont do anything?
// TODO: resort
//_rmm.resort({ContactGroupEphemeral{e.group_number}});
_rmm.throwEventUpdate(reg, matching_e);
}
} else {
// tmp, assume message new
matching_e = reg.create();
reg.emplace<Message::Components::ContactFrom>(matching_e, sync_c);
reg.emplace<Message::Components::ContactTo>(matching_e, sync_by_c.get<Contact::Components::Parent>().parent);
reg.emplace<Message::Components::ToxGroupMessageID>(matching_e, e.message_id);
reg.emplace<Message::Components::MessageText>(matching_e, e.message_text);
reg.emplace<Message::Components::TimestampProcessed>(matching_e, now_ts);
reg.emplace<Message::Components::TimestampWritten>(matching_e, sync_ts);
reg.emplace<Message::Components::Timestamp>(matching_e, sync_ts); // reactive?
// TODO: resort
//_rmm.resort({ContactGroupEphemeral{e.group_number}});
_rmm.throwEventConstruct(reg, matching_e);
}
{ // by whom
auto& synced_by = reg.get_or_emplace<Message::Components::SyncedBy>(matching_e).list;
synced_by.emplace(sync_by_c);
// TODO: throw update?
}
return true;
}
bool ZoxNGCHistorySync::onToxEvent(const Tox_Event_Group_Peer_Join* e) {
const auto group_number = tox_event_group_peer_join_get_group_number(e);
const auto peer_number = tox_event_group_peer_join_get_peer_id(e);
const auto c = _tcm.getContactGroupPeer(group_number, peer_number);
if (!_request_queue.count(c)) {
_request_queue[c] = {
_delay_before_first_request_min + _rng_dist(_rng)*_delay_before_first_request_add,
0.f,
130u // TODO: magic number
};
}
return false;
}

101
solanaceae/zox/ngc_hs.hpp Normal file
View File

@ -0,0 +1,101 @@
#pragma once
#include "./ngc.hpp"
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
#include <array>
#include <queue>
#include <map>
#include <random>
// fwd
struct ToxI;
struct ContactModelI;
class RegistryMessageModel;
class ToxContactModel2;
// zoff ngc history sync (draft1?)
// https://gist.github.com/zoff99/81917ddb2e55b2ce602cac4772a7b68c
class ZoxNGCHistorySync : public ToxEventI, public ZoxNGCEventI {
ToxEventProviderI& _tep;
ZoxNGCEventProviderI& _zngcepi;
ToxI& _t;
Contact3Registry& _cr;
ToxContactModel2& _tcm;
RegistryMessageModel& _rmm;
// how far apart the 2 timestamps can be, before they are considered different messages
const int64_t _max_age_difference_ms {130*60*1000}; // TODO: make this larger?
// 5s-11s
const float _delay_before_first_request_min {5.f};
const float _delay_before_first_request_add {6.f};
// 30m-64m
const float _delay_next_request_min {30.f*60.f};
const float _delay_next_request_add {64.f*60.f};
// 0.3s-0.6s
const float _delay_between_syncs_min {0.3f};
const float _delay_between_syncs_add {0.3f};
std::uniform_real_distribution<float> _rng_dist {0.0f, 1.0f};
std::minstd_rand _rng;
struct RequestQueueInfo {
float delay; // const
float timer;
uint8_t sync_delta;
};
// request queue
// c -> delay, timer
std::map<Contact3, RequestQueueInfo> _request_queue;
struct SyncQueueInfo {
float delay; // const
float timer;
std::queue<Message3> ents;
//std::reference_wrapper<Message1Registry> reg;
};
std::map<Contact3, SyncQueueInfo> _sync_queue;
// sync queue
void subscribeToEvents(void); // private
public:
ZoxNGCHistorySync(ToxEventProviderI& tep, ZoNGCEventProviderI& zngcepi, ToxI& t, Contact3Registry& cr, ToxContactModel2& tcm, RegistryMessageModel& rmm);
void tick(float delta);
public:
// always private
bool sendRequest(
uint32_t group_number, uint32_t peer_number,
uint8_t sync_delta = 130u
);
// always private
bool sendSyncMessage(
uint32_t group_number, uint32_t peer_number,
uint32_t message_id,
const std::array<uint8_t, 32>& sender_pub_key,
uint32_t timestamp,
std::string_view sender_name,
std::string_view message_text
);
protected:
bool onEvent(const Events::ZoxNGC_ngch_request& e) override;
bool onEvent(const Events::ZoxNGC_ngch_syncmsg& e) override;
protected:
bool onToxEvent(const Tox_Event_Group_Peer_Join* e) override;
//bool onToxEvent(const Tox_Event_Group_Peer_Exit* e) override;
//bool onToxEvent(const Tox_Event_Group_Custom_Packet* e) override;
//bool onToxEvent(const Tox_Event_Group_Custom_Private_Packet* e) override;
};