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