hello mr fox
This commit is contained in:
commit
b968c47de4
22
CMakeLists.txt
Normal file
22
CMakeLists.txt
Normal 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
24
LICENSE
Normal 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.
|
||||||
|
|
220
zox/ngc.cpp
Normal file
220
zox/ngc.cpp
Normal 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
zox/ngc.hpp
Normal file
121
zox/ngc.hpp
Normal 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
zox/ngc_hs.cpp
Normal file
412
zox/ngc_hs.cpp
Normal 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
zox/ngc_hs.hpp
Normal file
101
zox/ngc_hs.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user